Architecture of
MLP
A Multilayer Perceptron (MLP) is generalized from the perceptron by
adding one or more hidden layers between the input and output layers.
That is, an MLP has three major components:
- An input layer
- One or more hidden layers
- An output layer
Each layer consists of multiple neurons (nodes)
connected via weights, with nonlinear activation functions that capture
complex relationships between features and the target, thereby improving
the algorithm’s performance.
If an MLP has only one hidden layer, this MLP is usually called
shallow neural network. Deep neural
networks have two or more hidden layers.
In the subsequent sections, we will briefly introduce the theory of
neural networks with an emphasis on shallow networks. Some case studies
will also be included to demonstrate the application of neural network
models in regression and classification.
Mathematics of
MLP
For illustration, we use the following one-hidden-layer MLP
regression to explain the mathematical foundations of the algorithm. The
objective is to predict the target variable \(y\) using input features \(\{x_1, x_2, \cdots, x_n \}\). If activation
functions \(\phi_1(\cdot)\) and \(\phi(\cdot)\) are correctly specified, then
the target can be expressed as a composition of weights through function
composition.

In essence, MLP is a problem of approximating a multi-variable
function. Assume that there are \(n\)
features in the scenario of the above one-hidden-layer MLP, the
relationship between the target variable \(y\) is characterized by \(y = f(x_1, x_2, \cdots, x_n: \mathbf{w})\),
where \(f(\cdot)\) is unknown and \(\mathbf{w}\) is the vector of unknown
parameters. That is, in the above MLP, the target can be expressed
as
\[
y = f(x_1, x_2, \cdots, x_n: \mathbf{w}) = \phi_2[\phi_1(x_1, x_2,
\cdots, x_n: \mathbf{w})].
\]
Note that \(f(\cdot)\) is an arbitrary function \(f: \mathbb{R}^n \rightarrow
\mathbb{R}\). For a given data set \(\{(x_{1i}, x_{2i}, \cdots,
x_{ni})\}_{i=1}^n\) and estimated \(\hat{\mathbf{w}}\), depending on
applications, the loss function is defined respectively by
- Continuous Regression (MSE)
\[
\mathcal{L} = \frac{1}{N} \sum_{c=1}^C (y_i - \hat{y}_i)^2
\]
- Discrete Classification (Cross-entropy)
\[
\mathcal{L} = - \sum_{c=1}^C y_c \log(\hat{y}_c)
\] for \(C\) classes. Using the
gradient decent method, we can express the weight updating formula in
the following
\[
\mathbf{W}^{(j+1)} \leftarrow \mathbf{W}^{(j)} - \eta \frac{\partial
\mathcal{L}}{\partial \mathbf{W}}\Big|_{\mathbf{W}=\mathbf{W}^{(j)}}
\] where \(\eta\) is the
hyperparameter of learning rate.
Universal
Approximation Theorem
The Universal Approximation Theorem is a fundamental
result in the theory of artificial neural networks, stating that a
feedforward neural network with a single hidden layer containing a
finite number of neurons can approximate continuous functions on compact
subsets of \(\mathbb{R}^n\) under mild
conditions on the activation function. The formal statement is given in
the following:
Cybenko’s Theorem (1989): Let \(\phi(\cdot)\) be a continuous, non-constant
activation function (e.g., sigmoid). Given any continuous function \(f: [0,1]^n \rightarrow \mathbb{R}\) and
\(\epsilon > 0\), there exists a
single-hidden-layer MLP (multilayer perceptron) with
finitely many hidden nodes (neurons), denoted by \(N(\mathbf{x})\), such that
\[
\sup_{\mathbf{x}\in [0,1]^n} \big|f(\mathbf{x}) - N(\mathbf{x}) \big|
< \epsilon.
\]
The above analysis assumes the support of the multivariable function
is \([0,1]^n\). This can be easily
generalized to any function with general finite support \([a-1, b_1]\times [a_2, b_2] \times
\cdots\times[a_k, b_k]\) via a linear transformation. For
example, assume \(f_k(x_k)\) has
support \([a_k, b_k]\), we can use
linear transformation \(y_k = (x_k - a_k)/(b_k
- a_k)\) which implies that \(y_k \in
[0,1]\).
This explains why a one-hidden-layer perceptron can
approximate any continuous function defined on a finite support using
finitely many nodes in the hidden layer. For functions with infinite
support, we can first approximate them on a finite domain and then apply
the universal approximation theorem to achieve the
desired result.
Recall that, the objective of regression analysis is to estimate the
unknown regression function based on a sample taken
from the underlying population and then use the
estimated regression function to
- assess the relationship between feature variables and the target
variable;
- predict the value of the target variable based on the values of the
input feature variables.
The universal approximation theorem for shallow
networks guarantees their capability as a function
approximator, making it a natural theoretical foundation for
neural network regression methods.
Some Graphical
Demonstrations
This subsection uses a simulation approach to demonstrate why MLP is
also a non-classical family of algorithmic regression models. We take a
set of points on the surface of a hypothesized elliptic paraboloid
regression surface and then use MLP to estimate the hypothetical
elliptic paraboloid model. The elliptic paraboloid used to generate data
is given by
\[
z = x_1^2 + x_2^2
\] The following figure shows the hypothetical regression
surface, along with the data points used to estimate it.
#library(plotly)
# Define the elliptic paraboloid function: z = (x^2 / a^2) + (y^2 / b^2)
# Create a grid of x and y values
x <- seq(-2, 2, length =200)
y <- seq(-2, 2, length =200)
z <- outer(x, y, function(x, y) (x^2 + y^2 ))
## sample points on the surface to define a data set
x.sim <- sample(x)
y.sim <- sample(y)
z.sample <- (x.sim^2 + y.sim^2) + rnorm(200, 0, 0.5)
# Plot margins
m <- list( l = 50, r = 50, b = 100, t = 100, pad = 4)
# Create the 3D surface plot
plot_ly(x = x, y = y, z = z, type = "surface") %>% # the surface
add_markers(x = x.sim, # sample points near the surface
y = y.sim,
z = z.sample,
marker = list(size = 3, color = "darkred")) %>%
layout(margin = m,
title = "Elliptic Paraboloid: Hypothesized Model for Data Generation",
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")
)
)
In practice, we only observe data points from an unknown surface. We
therefore remove the hypothetical regression surface and visualize just
the data points to reveal the data cloud’s structure.
plot_ly() %>%
add_markers(x = x.sim, y = y.sim, z = z.sample,
marker = list(size = 3, color = "darkred")) %>%
layout( margin = m,
title = "Sample points from the elliptic paraboloid",
scene = list( xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")
)
)
There is a nonlinear relationship between the target variable (\(z\)) and feature variables (\(x_1\) and \(x_2\)). In the following figures, we
compare the performance of three models fitted to the same dataset: a
single perceptron, a one-hidden-layer perceptron, and a two-hidden-layer
perceptron. We then plot their corresponding estimated regression
surfaces to visualize the goodness of fit.
#library(neuralnet)
#library(ggplot2)
#library(plotly) # For 3D visualization
# Generate training data
#set.seed(123)
#x <- seq(-2, 2, length =20)
#y <- seq(-2, 2, length =20)
#z_train <- x_train^2 + y_train^2 # + rnorm(500, sd = 0.1) # Added noise
## The working data set: we call it the training data set
train.data <- data.frame(x = x.sim, y = y.sim, z = z.sample)
### no hidden layer
hidden0.0 <- neuralnet(z ~ x + y,
data = train.data,
hidden = c(0),
linear.output = TRUE,
stepmax = 1e6) # Increase iterations
### single hidden layer
hidden1.8 <- neuralnet(z ~ x + y,
data = train.data,
hidden = c(8),
linear.output = TRUE,
stepmax = 1e6) # Increase iterations
# Train neural network with 2 hidden layers (8 and 4 neurons)
hidden2.8.4 <- neuralnet(z ~ x + y,
data = train.data,
hidden = c(8,4),
linear.output = TRUE,
stepmax = 1e6) # Increase iterations
# Create test grid to draw the Predicted surface based on different NN models
x.test <- seq(-2, 2, length.out = 200)
y.test <- seq(-2, 2, length.out = 200)
test.grid <- expand.grid(x = x.test, y = y.test)
z.test0.0 <- matrix(predict(hidden0.0, test.grid),ncol=200)
z.test1.8 <- matrix(predict(hidden1.8, test.grid),ncol=200)
z.test2.8.4 <- matrix(predict(hidden2.8.4, test.grid),ncol=200)
The following figure shows the predicted surface from the perceptron
model, which produces a linear hyperplane, along with the data points
used to estimate the regression surface. The perceptron fits the data
poorly.
## set up plot margin
m <- list(l = 50,r = 50, b = 100, t = 100, pad = 4)
# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test0.0, type = "surface") %>%
add_markers(data = train.data,
x = ~x, y = ~y, z = ~z,
marker = list(size = 3, color = "darkred")) %>%
layout(title = 'Perceptron Model (with no hidden layer)',
autosize = T,
#width = 500,
#height = 500,
margin = m,
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")))
Next, we fit a one-hidden-layer perceptron with 8
nodes to the same dataset. The following figure shows both the
estimated regression surface and the data points. We observe that the
predicted surface closely approximates the hypothesized
regression surface. Note that the number of nodes in the hidden
layer is a tunable hyperparameter that can be optimized to improve model
performance.
# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test1.8, type = "surface") %>%
add_markers(data = train.data,
x = ~x, y = ~y, z = ~z,
marker = list(size = 3, color = "darkred")) %>%
layout(title = 'Estimated One-hidden-layer Perceptron (with 8 nodes)',
autosize = T,
#width = 500,
#height = 500,
margin = m,
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")))
Finally, we examine a two-hidden-layer perceptron architecture, with
8 nodes in the first hidden layer and 4 nodes in the second hidden
layer, trained on the same data set.
# 3D Visualization with plotly
plot_ly(x = ~x.test, y = ~y.test, z = ~z.test2.8.4, type = "surface") %>%
add_markers(data = train.data,
x = ~x, y = ~y, z = ~z,
marker = list(size = 3, color = "darkred")) %>%
layout(title = 'Estimated Two-hidden-layer Perceptron (with 8 and 4 nodes)',
autosize = T,
#width = 500,
#height = 500,
margin = m,
scene = list(xaxis = list(title = "X"),
yaxis = list(title = "Y"),
zaxis = list(title = "Z")))
The figure above shows both the estimated prediction surface and the
original data points. We observe a significant discrepancy between the
estimated regression surface and the
hypothesized (true) regression surface. Upon closer
examination, we find that the estimated regression surface yields small
residuals, as the data points lie close to the fitted surface. This
suggests potential overfitting in the two-hidden-layer
perceptron model.
MLP Regression
This section focuses on implementing MLP regression using the Boston
Housing dataset, along with the R neuralnet library and
other supporting libraries for data preparation and visualization. We
will follow the basic steps outlined in the previous section. We will
fit one-hidden-layer and two-hidden-layer MLP to predict the median
house value.
We use min-max scaling method for all numerical
variables. All features are numerical, no categorical encoding is
needed. We use random splitting (70-30) to create training and testing
data sets.
# Load necessary libraries
# library(neuralnet)
# library(MASS) # For Boston Housing dataset
# library(ggplot2) # For visualization
# library(caret) # Only for data splitting (we won't use its modeling functions)
# Load Boston Housing dataset
data(Boston)
# Check structure and summary
#str(Boston)
#summary(Boston)
# Feature scaling - normalize all variables to [0,1] range
normalize <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
boston.scaled <- as.data.frame(lapply(Boston, normalize))
# Set seed for reproducibility
set.seed(123)
N <- length(boston.scaled$medv)
# Create train-test split (70-30)
train.reg.index <- sample(1:N, floor(0.7*N), replace = FALSE)
train.reg.data <- boston.scaled[train.reg.index, ]
test.reg.data <- boston.scaled[-train.reg.index, ]
# Check dimensions
#dim(train_data)
#dim(test_data)
One-hidden-layer
Perceptron
We first build a single hidden layer perceptron model. We will tune
three hyperparameters: the number of nodes in the hidden layer, learning
rate, and activation function using grid search. The performance metric
used to select the optimal combination of values of hyperparameters is
RMSE.
# Define grid of hyperparameters
hyper.grid.reg <- expand.grid(
layer1 = c(5, 10, 15),
learning.rate = c(0.01, 0.1),
activation = c("logistic", "tanh")
)
# Initialize results storage
rmse = NULL
#layer1 = NULL
#learningrate = NULL
#activation = NULL
best.reg.rmse <- Inf
best.reg.model <- NULL
# Perform grid search
for(i in 1:nrow(hyper.grid.reg)) {
# Get current configuration
layer <- hyper.grid.reg$layer1[i]
lr <- hyper.grid.reg$learning.rate[i]
act <- hyper.grid.reg$activation[i]
# Train model
set.seed(123)
model.reg <- neuralnet(
medv ~ .,
data = train.reg.data,
hidden = layer,
act.fct = act,
linear.output = TRUE, # For regression
learningrate = lr,
algorithm = "rprop+",
stepmax = 1e5 )
# Make predictions
preds.reg <- predict(model.reg, test.reg.data[, -ncol(test.reg.data)])
# Calculate RMSE
rmse.reg <- sqrt(mean((preds.reg - test.reg.data$medv)^2))
# Store results
rmse[i] = rmse.reg
# Update best model
if(rmse.reg < best.reg.rmse) {
best.reg.rmse <- rmse.reg
best.reg.model <- model.reg
best.reg.params <- hyper.grid.reg[i, ]
}
}
results.regNN <- hyper.grid.reg
results.regNN$rmse <- rmse
# View results sorted by RMSE
pander(results.regNN[order(results.regNN$rmse), ][1,])
| 3 |
15 |
0.01 |
logistic |
0.08035 |
With the above combination of optimal hyperparameter values, we train
the final single hidden layer perceptron model.
# Train the final model with best parameters
final.reg.model <- neuralnet(
medv ~ .,
data = train.reg.data,
hidden = best.reg.params$layer1,
act.fct = best.reg.params$activation,
linear.output = TRUE,
learningrate = best.reg.params$learning_rate,
algorithm = "rprop+",
stepmax = 1e5
)
# Plot the neural network
plot(final.reg.model)
The above NN plot shows the architecture of the final
one-hidden-layer perceptron model. Next, we will use it to make
predictions.
# Make predictions on test set
pred.NN1 <- predict(final.reg.model, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
rmse.NN1 <- sqrt(mean((pred.NN1 - test.reg.data$medv)^2))
mae.NN1 <- mean(abs(pred.NN1 - test.reg.data$medv))
r_squared.NN1 <- cor(pred.NN1 , test.reg.data$medv)^2
# cat("Performance Metrics:\n")
# cat("RMSE:", rmse, "\n")
# cat("MAE:", mae, "\n")
# cat("R-squared:", r_squared, "\n")
# Plot predictions vs actual
plot.NN1.data <- data.frame(
Actual = test.reg.data$medv,
Predicted = pred.NN1
)
ggplot(plot.NN1.data, aes(x = Actual, y = Predicted)) +
geom_point() +
geom_abline(intercept = 0, slope = 1, color = "darkred") +
annotate("text", x=0.85, y=.2,
label=paste("R.sq =", round(r_squared.NN1,4)), color="blue") +
annotate("text", x=0.85, y=.13,
label=paste("RMSE =", round(rmse.NN1,4)), color="blue") +
annotate("text", x=0.85, y=.06,
label=paste(" MAE =", round(mae.NN1,4)), color="blue") +
ggtitle("Actual vs Predicted Values") +
theme_minimal()

The figure above demonstrates a strong correlation between the
predicted target values and the scaled target values. Both the mean
squared error (MSE) and mean absolute error (MAE) metrics are displayed
on the plot. Next, we fit a classical linear regression model to the
scaled data and perform a comparative analysis between this baseline
linear regression and our neural network model.
# library(ggplot2)
# Train linear regression model
lm_model <- lm(medv ~ ., data = test.reg.data)
# Make predictions
lm_predictions <- predict(lm_model, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
lm_rmse <- sqrt(mean((lm_predictions - test.reg.data$medv)^2))
lm_mae <- mean(abs(lm_predictions - test.reg.data$medv))
lm_r_squared <- cor(lm_predictions, test.reg.data$medv)^2
## improvements
RMSE.imp <- round((lm_rmse - rmse.NN1)/lm_rmse * 100,2)
MAE.imp <- round((lm_mae - mae.NN1)/lm_mae * 100, 2)
Rsq.imp <- round((r_squared.NN1 - lm_r_squared)/lm_r_squared * 100,2)
##
Performance.table <- data.frame(
LM = c(lm_rmse, lm_mae, lm_r_squared),
NN.1 = c(rmse.NN1, mae.NN1, r_squared.NN1),
Improvement.percentage = c(RMSE.imp, MAE.imp, Rsq.imp)
)
rownames(Performance.table) <- c("RMSE", "MAE", "R.square")
pander(Performance.table)
| RMSE |
0.09682 |
0.08986 |
7.19 |
| MAE |
0.07097 |
0.0565 |
20.39 |
| R.square |
0.7858 |
0.8299 |
5.61 |
# Plot both predictions
comparison.data <- data.frame(
Actual = test.reg.data$medv,
MLP = pred.NN1,
Linear = lm_predictions
)
ggplot(comparison.data) +
geom_point(aes(x = Actual, y = MLP, color = "MLP")) +
geom_point(aes(x = Actual, y = Linear, color = "Linear Regression")) +
geom_abline(intercept = 0, slope = 1, color = "black") +
scale_color_manual(values = c("MLP" = "blue", "Linear Regression" = "red")) +
labs(title = "Model Comparison: Actual vs Predicted",
x = "Actual Values",
y = "Predicted Values",
color = "Model Type") +
theme(
plot.margin = ggplot2::margin(40, 20, 20, 20, unit = "pt"),
plot.title = element_text(hjust = 0.5,
lineheight = 1.1,
vjust = 10)
)

The above scatter plot of the true target values and predicted values
based on the two models also shows that the one-hidden-perceptron model
outperforms the classic linear regression model.
Two-hidden-layer
Perceptron
This subsection explores whether a two-hidden-layer perception that
has more complex architecture could improve the performance.
The model-building process is identical to the previous
one-hidden-layer perceptron model. We will not detail the steps as we
did in the previous section.
# Define grid of hyperparameters
hyper.grid.NN2 <- expand.grid(
layer1 = c(5, 10, 15),
layer2 = c(0, 3, 5), # 0 means no second layer
learning.rate = c(0.01, 0.1),
activation = c("logistic", "tanh")
)
# Initialize results in storage
results <- data.frame()
best.rmse <- Inf
best.model <- NULL
# Perform grid search
for(i in 1:nrow(hyper.grid.NN2)) {
# Get current configuration
layer1 <- hyper.grid.NN2$layer1[i]
layer2 <- hyper.grid.NN2$layer2[i]
lr <- hyper.grid.NN2$learning.rate[i]
act <- hyper.grid.NN2$activation[i]
# Create hidden layers vector
if(layer2 == 0) {
hidden.layers <- c(layer1)
} else {
hidden.layers <- c(layer1, layer2)
}
# Train model
set.seed(123)
model.NN2 <- tryCatch({
neuralnet(
medv ~ .,
data = train.reg.data,
hidden = hidden.layers,
act.fct = act,
linear.output = TRUE, # For regression
learningrate = lr,
algorithm = "rprop+",
stepmax = 1e5
)
}, error = function(e) NULL)
if(!is.null(model.NN2)) {
# Make predictions
preds <- predict(model.NN2, test.reg.data[, -ncol(test.reg.data)])
# Calculate RMSE
rmse <- sqrt(mean((preds - test.reg.data$medv)^2))
# Store results
results <- rbind(results, data.frame(
layer1 = layer1,
layer2 = layer2,
learning_rate = lr,
activation = act,
rmse = rmse
))
# Update best model
if(rmse < best.rmse) {
best.rmse <- rmse
best.model <- model.NN2
best.params <- hyper.grid.NN2[i, ]
}
}
}
# View results sorted by RMSE
results[order(results$rmse), ]
layer1 layer2 learning_rate activation rmse
22 5 3 0.01 tanh 0.07419751
31 5 3 0.10 tanh 0.07419751
3 15 0 0.01 logistic 0.08034564
12 15 0 0.10 logistic 0.08034564
4 5 3 0.01 logistic 0.08647528
13 5 3 0.10 logistic 0.08647528
9 15 5 0.01 logistic 0.08715168
18 15 5 0.10 logistic 0.08715168
8 10 5 0.01 logistic 0.08804799
17 10 5 0.10 logistic 0.08804799
23 10 3 0.01 tanh 0.08948137
32 10 3 0.10 tanh 0.08948137
7 5 5 0.01 logistic 0.09090198
16 5 5 0.10 logistic 0.09090198
24 15 3 0.01 tanh 0.09167677
33 15 3 0.10 tanh 0.09167677
20 10 0 0.01 tanh 0.09223560
29 10 0 0.10 tanh 0.09223560
5 10 3 0.01 logistic 0.09638812
14 10 3 0.10 logistic 0.09638812
25 5 5 0.01 tanh 0.09706433
34 5 5 0.10 tanh 0.09706433
27 15 5 0.01 tanh 0.09768202
36 15 5 0.10 tanh 0.09768202
6 15 3 0.01 logistic 0.10032486
15 15 3 0.10 logistic 0.10032486
19 5 0 0.01 tanh 0.10290197
28 5 0 0.10 tanh 0.10290197
1 5 0 0.01 logistic 0.10499690
10 5 0 0.10 logistic 0.10499690
2 10 0 0.01 logistic 0.10531639
11 10 0 0.10 logistic 0.10531639
21 15 0 0.01 tanh 0.15333090
30 15 0 0.10 tanh 0.15333090
26 10 5 0.01 tanh 0.15473522
35 10 5 0.10 tanh 0.15473522
# Train the final model with best parameters
final.model.NN2 <- neuralnet(
medv ~ .,
data = train.reg.data,
hidden = if(best.params$layer2 == 0) {
c(best.params$layer1)} else {
c(best.params$layer1, best.params$layer2)},
act.fct = best.params$activation,
linear.output = TRUE,
learningrate = best.params$learning.rate,
algorithm = "rprop+",
stepmax = 1e5
)
# Plot the neural network
plot(final.model.NN2)
# Make predictions on test set
pred.NN2 <- predict(final.model.NN2, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
rmse.NN2 <- sqrt(mean((pred.NN2 - test.reg.data$medv)^2))
mae.NN2 <- mean(abs(pred.NN2 - test.reg.data$medv))
r_squared.NN2 <- cor(pred.NN2 , test.reg.data$medv)^2
## vector of error metric
NN2 <- c(rmse.NN2, mae.NN2, r_squared.NN2)
# Plot predictions vs actual
plot.data.NN2 <- data.frame(
Actual = test.reg.data$medv,
Predicted = pred.NN2
)
ggplot(plot.data.NN2, aes(x = Actual, y = Predicted)) +
geom_point() +
geom_abline(intercept = 0, slope = 1, color = "red") +
ggtitle("Actual vs Predicted Values") +
theme_minimal()

# Train linear regression model
lm_model <- lm(medv ~ ., data = train.reg.data)
# Make predictions
lm_predictions <- predict(lm_model, test.reg.data[, -ncol(test.reg.data)])
# Calculate performance metrics
lm_rmse <- sqrt(mean((lm_predictions - test.reg.data$medv)^2))
lm_mae <- mean(abs(lm_predictions - test.reg.data$medv))
lm_r_squared <- cor(lm_predictions, test.reg.data$medv)^2
###
rmse.NN2.imp <- (lm_rmse - rmse.NN2)/lm_rmse*100
mae.NN2.imp <- (lm_mae - mae.NN2)/lm_mae * 100
Rsq.NN2.imp <- (r_squared.NN2 - lm_r_squared)/lm_r_squared * 100
NN2.improve <-c(rmse.NN2.imp, mae.NN2.imp, Rsq.NN2.imp)
perf.matrix <-data.frame(
LM = c(lm_rmse, lm_mae, lm_r_squared),
NN.1 = c(rmse.NN1, mae.NN1, r_squared.NN1),
NN.2 = c(rmse.NN2, mae.NN2, r_squared.NN2)
)
perf.matrix$NN1.Improve <- round(100*(perf.matrix$LM-perf.matrix$NN.1)/perf.matrix$LM,2)
perf.matrix$NN2.Improve <- round(100*(perf.matrix$LM-perf.matrix$NN.2)/perf.matrix$LM,2)
rownames(perf.matrix) <- c("RMSE", "MAE", "R.sq")
pander(perf.matrix)
| RMSE |
0.1067 |
0.08986 |
0.09108 |
15.81 |
14.67 |
| MAE |
0.07848 |
0.0565 |
0.05954 |
28.01 |
24.13 |
| R.sq |
0.7461 |
0.8299 |
0.8171 |
-11.23 |
-9.51 |
# Plot both predictions
comparison.data.NN2 <- data.frame(
Actual = test.reg.data$medv,
MLP = pred.NN2 ,
Linear = lm_predictions
)
ggplot(comparison.data.NN2) +
geom_point(aes(x = Actual, y = MLP, color = "MLP")) +
geom_point(aes(x = Actual, y = Linear, color = "Linear Regression")) +
geom_abline(intercept = 0, slope = 1, color = "black") +
scale_color_manual(values = c("MLP" = "blue", "Linear Regression" = "red")) +
labs(title = "Model Comparison: Actual vs Predicted",
x = "Actual Values",
y = "Predicted Values",
color = "Model Type") +
theme_minimal()

MLP Classification
The modeling process for MLP classification is identical to that of
MLP regression. As we did in the previous section on MLP regression, we
will document the modeling process in detail.
The Pima Indian Diabetes dataset contains only
numerical feature variables. Therefore, we will apply min-max scaling to
all features and convert the target variable into a factor variable.
# Load necessary libraries
# library(neuralnet)
# library(pROC) # For ROC analysis
# library(ggplot2) # For visualization
# Load the Pima Indians Diabetes dataset
data("PimaIndiansDiabetes2", package = "mlbench")
## removing records with missing component; imputation should be considered in
## practical applications
diabetes.data <- na.omit(PimaIndiansDiabetes2)
# Feature scaling - normalize numeric variables to [0,1] range
normalize <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
# Apply normalization to all numeric columns
numeric.cols <- sapply(diabetes.data, is.numeric)
diabetes.data[numeric.cols] <- lapply(diabetes.data[numeric.cols], normalize)
# Encode the target variable (diabetes) as numeric (0/1)
diabetes.data$diabetes <- ifelse(diabetes.data$diabetes == "pos", 1, 0)
# Two-way data splitting: 70-30%
set.seed(123) # For reproducibility
sample.size.cls <- floor(0.70 * nrow(diabetes.data))
train.indices.cls <- sample(1:sample.size.cls, size = sample.size.cls, replace = FALSE)
train.data.cls <- diabetes.data[train.indices.cls, ]
test.data.cls <- diabetes.data[-train.indices.cls, ]
To simplify hyperparameter tuning and final model training with the
pre-selected MLP architecture for classification, we define a custom
function to determine the optimal number of nodes for both
single-hidden-layer and double-hidden-layer MLPs. The criterion for
selecting the optimal number of nodes is the area under the ROC curve
(AUC) as the evaluation metric, though accuracy based on the default 0.5
thresholds could alternatively be used. We choose the logistic
activation function while keeping all other hyperparameters at their
default values.
# Function to perform grid search for neuralnet
neuralnet.grid.search <- function(train.data, test.data, hidden.layers = 1) {
# Define the grid of hyperparameters
if (hidden.layers == 1) {
hidden.nodes <- c(2, 4, 6, 8, 10)
grid <- expand.grid(hidden = hidden.nodes)
} else {
hidden.nodes <- c(2, 4, 6)
grid <- expand.grid(hidden1 = hidden.nodes, hidden2 = hidden.nodes)
}
# Add columns to store results
grid$accuracy <- NA
grid$auc <- NA
# Formula for neural network
nn.formula <- as.formula(paste("diabetes ~",
paste(names(train.data)[!names(train.data) %in% "diabetes"],
collapse = " + ")))
# Perform grid search
for (i in 1:nrow(grid)) {
if (hidden.layers == 1) {
hidden <- c(grid$hidden[i])
} else {
hidden <- c(grid$hidden1[i], grid$hidden2[i])
}
# Train the model
nn.model <- neuralnet(
formula = nn.formula,
data = train.data,
hidden = hidden,
linear.output = FALSE, # For classification
act.fct = "logistic", # Sigmoid activation
stepmax = 1e6 # Increase max steps for convergence
)
# Make predictions
predictions <- predict(nn.model, test.data)
predicted.classes <- ifelse(predictions > 0.5, 1, 0)
# Calculate accuracy
accuracy <- mean(predicted.classes == test.data$diabetes)
# Calculate AUC
roc.obj <- roc(test.data$diabetes, predictions)
auc.value <- auc(roc.obj)
# Store results
grid$accuracy[i] <- accuracy
grid$auc[i] <- auc.value
}
return(grid)
}
The performance table of corresponding number of nodes in
one-hidden-layer MLP is given below.
# Perform grid search for single hidden layer
grid.results.1layer <- neuralnet.grid.search(train.data=train.data.cls,
test.data=test.data.cls,
hidden.layers = 1)
pander(grid.results.1layer)
| 2 |
0.8305 |
0.8569 |
| 4 |
0.822 |
0.8659 |
| 6 |
0.839 |
0.8509 |
| 8 |
0.8559 |
0.8941 |
| 10 |
0.822 |
0.873 |
The optimal number of nodes in the hidden layer is the corresponds to
the smallest AUC. Similarly, the performance table of two-hidden-layer
MLP is given below.
# Perform grid search for two hidden layers
grid.results.2layer <- neuralnet.grid.search(train.data=train.data.cls,
test.data=test.data.cls,
hidden.layers = 2)
pander(grid.results.2layer)
| 2 |
2 |
0.8305 |
0.856 |
| 4 |
2 |
0.822 |
0.8879 |
| 6 |
2 |
0.822 |
0.8742 |
| 2 |
4 |
0.8136 |
0.8166 |
| 4 |
4 |
0.7881 |
0.8318 |
| 6 |
4 |
0.8136 |
0.8979 |
| 2 |
6 |
0.8051 |
0.8956 |
| 4 |
6 |
0.7797 |
0.8552 |
| 6 |
6 |
0.8644 |
0.9087 |
One-hidden-layer MLP
We use the optimal number of nodes to fitthe onehidden-layer MLP to
the data anf obtain
# Formula for neural network
nn.formula <- as.formula(paste("diabetes ~",
paste(names(train.data.cls)[!names(train.data.cls) %in% "diabetes"],
collapse = " + ")))
# Train single hidden layer model (using best configuration from grid search)
best.1layer <- grid.results.1layer[which.max(grid.results.1layer$auc), ]
nn.1layer <- neuralnet(
formula = nn.formula,
data = train.data.cls,
hidden = best.1layer$hidden,
linear.output = FALSE,
act.fct = "logistic",
stepmax = 1e6
)
##
plot(nn.1layer, main = paste("One-hidden-layer with", best.1layer$hidden, "Nodes"))
# Train two hidden layers model (using best configuration from grid search)
best.2layer <- grid.results.2layer[which.max(grid.results.2layer$auc), ]
nn.2layer <- neuralnet(
formula = nn.formula,
data = train.data.cls,
hidden = c(best.2layer$hidden1, best.2layer$hidden2),
linear.output = FALSE,
act.fct = "logistic",
stepmax = 1e6
)
##
plot(nn.2layer)
In the two model plots above, the Error and
Steps values displayed at the bottom represent:
The number of training iterations (epochs) completed
during model optimization. Each step corresponds to one complete
forward/backward pass and weight update cycle.
The training error reflects the loss function value
(typically SSE for regression or cross-entropy for classification). The
displayed Error represents the final error value
achieved when the optimization process converges.
Next, we write a custom function to extract the performance metrics
to assess the global performance through ROC curves and the
corresponding areas under the ROC curves.
# Function to evaluate model performance
evaluate.model <- function(model, test.data) {
# Make predictions
predictions <- predict(model, test.data)
predicted.classes <- ifelse(predictions > 0.5, 1, 0)
# Calculate metrics
accuracy <- mean(predicted.classes == test.data$diabetes)
confusion.matrix <- table(Predicted = predicted.classes, Actual = test.data$diabetes)
roc.obj <- roc(test.data$diabetes, predictions)
auc.value <- auc(roc.obj)
return(list(
accuracy = accuracy,
confusion.matrix = confusion.matrix,
roc.obj = roc.obj,
auc = auc.value
))
}
# Evaluate single hidden layer model
perf.1layer <- evaluate.model(nn.1layer, test.data.cls)
#print(perf.1layer[c("accuracy", "confusion_matrix", "auc")])
# Evaluate two hidden layers model
perf.2layer <- evaluate.model(nn.2layer, test.data.cls)
#print(perf.2layer[c("accuracy", "confusion_matrix", "auc")])
We use classic logistic regression as the base model and compare it
with the two MLPs using ROC curves and their corresponding AUC values in
the following figure.
# Train logistic regression model (base model)
logit.model <- glm(diabetes ~ ., data = train.data.cls, family = binomial)
# Evaluate logistic regression model
logit.pred <- predict(logit.model, test.data.cls, type = "response")
logit.classes <- ifelse(logit.pred > 0.5, 1, 0)
logit.accuracy <- mean(logit.classes == test.data.cls$diabetes)
logit.roc <- roc(test.data.cls$diabetes, logit.pred)
logit.auc <- auc(logit.roc)
##
roc.1layer <- perf.1layer$roc.obj
roc.2layer <- perf.2layer$roc.obj
roc.logit <- logit.roc
## specificity and sensitivity
sen.1layer <- roc.1layer$sensitivities
spe.1layer <- roc.1layer$specificities
sen.2layer <- roc.2layer$sensitivities
spe.2layer <- roc.2layer$specificities
sen.logit <- roc.logit$sensitivities
spe.logit <- roc.logit$specificities
## AUC
auc.1layer <- roc.1layer$auc
auc.2layer <- roc.2layer$auc
auc.logit <- roc.logit$auc
## Plot ROC curves for comparison
par(pty = "s") # make a square plot to avaoid distortion
plot(1-spe.1layer, sen.1layer, type = "l", lty = 1,
col = "blue",
xlab = "1 - specificity",
ylab = "sensitvity",
main = "ROC Curve Comparison")
lines(1-spe.2layer, sen.2layer, lty = 1, col = "darkred")
lines(1-spe.logit, sen.logit, lty = 1, col = "darkgreen")
legend("bottomright",
legend = c(paste("1-layer MLP (AUC =", round(perf.1layer$auc, 3), ")"),
paste("2-layer MLP (AUC =", round(perf.2layer$auc, 3), ")"),
paste("Logistic Reg (AUC =", round(logit.auc, 3), ")")),
col = c("blue", "darkred", "darkgreen"),
lty = 1, cex = 0.7, bty = "n")

The ROC analysis shows that the classic logistic regression model
performs slightly better than the two MLPs. The two MLPs perform equally
well, even though the two-hidden-layer MLP has more parameters (weights)
and is more complex than the single-hidden-layer MLP. In general, a
single-hidden-layer MLP is recommended if an MLP is used for
classification.
MLP or Classical
Models?
We have discussed the MLP architecture and the process of
implementing MLPs alongside classic models. Case studies using the
Boston Housing Data demonstrate that MLP regression
outperforms classic linear regression, while the classic logistic
regression performs better than MLP classification in the Pima
Indian Diabetes dataset analysis. Next, we will present a
general comparison between these models.
MLP Classification vs. Logistic Regression
The comparison between multilayer perceptron (MLP) classifiers and
logistic regression highlights a fundamental trade-off between
flexibility and simplicity.
Logistic regression, as a linear classifier, performs well when
decision boundaries are approximately linear, offering high
interpretability, computational efficiency, and robustness to
overfitting—particularly with limited data.
MLPs, with their nonlinear activation functions, can model more
complex decision boundaries but require careful tuning of architecture
and regularization to avoid overfitting.
The added complexity of neural networks may not always be
justified for simpler classification tasks. However, MLPs remain
valuable when dealing with highly nonlinear data where logistic
regression’s linear assumptions fail.
MLP Regression vs. Classic Linear Regression
Similar to their classification counterparts, MLP regression models
and classical linear regression serve different purposes depending on
the nature of the data.
Linear regression is optimal when relationships between
predictors and the target variable are linear, providing interpretable
coefficients, fast training, and low risk of overfitting.
MLP regression, however, excels in capturing nonlinear and
interactive effects, making it suitable for more complex regression
tasks where linear models underperform.
The trade-off lies in computational cost, tuning complexity, and
the need for larger datasets to prevent overfitting. If
the underlying data structure is unknown, a practical approach is to
begin with linear regression as a baseline before considering MLPs if
residuals suggest unmodeled nonlinearity.
Single-Hidden-Layer vs. Two-Hidden-Layer MLPs
The comparison between single- and
two-hidden-layer MLPs underscores model complexity.
Deeper networks theoretically have greater
representational power. Case studies found that the
two-hidden-layer MLP did not outperform its simpler
counterpart, despite having more parameters. This is consistent with the
universal approximation theorem, which states that a single hidden layer
(with sufficient neurons) can approximate any continuous
function.
The additional layer introduced unnecessary complexity without
gains in accuracy, reinforcing that deeper architectures should only be
explored when simpler models prove inadequate—typically in highly
nonlinear or high-dimensional problems.
General Recommendations
Start simple: Always begin with classical models
(linear/logistic regression) as baselines. Their interpretability and
efficiency make them ideal for initial exploration.
Use MLPs judiciously: Reserve MLPs for cases
where linear models underperform due to clear nonlinearity or
interaction effects. Prefer single-hidden-layer architectures unless
deeper networks demonstrably improve performance.
Balance complexity and performance: Avoid
unnecessary model complexity—favor the simplest model that achieves
satisfactory accuracy.
Hybrid approach: Combine linear models with
feature engineering or kernel methods before resorting to MLPs,
particularly in resource-constrained settings.
Ultimately, model choice should be guided by problem constraints,
data structure, and the trade-offs between accuracy, interpretability,
and computational cost. A systematic, iterative approach—from linear
models to shallow then deep networks—ensures both efficiency and
performance.
LS0tDQp0aXRsZTogJ011bHRpbGF5ZXIgTmV1cmFsIE5ldHdvcmtzIGZvciBDbGFzc2lmaWNhdGlvbiBhbmQgUmVncmVzc2lvbicNCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHs9aHRtbH0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovDQoNCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtc2l6ZTogMjJweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgDQojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCmlmICghcmVxdWlyZSgicGFsbWVycGVuZ3VpbnMiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiKQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCn0NCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiZTEwNzEiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZTEwNzEiKQ0KbGlicmFyeShlMTA3MSkNCn0NCmlmICghcmVxdWlyZSgibW1lbG4iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibW1lbG4iKQ0KbGlicmFyeShtbWVsbikNCn0NCmlmICghcmVxdWlyZSgiTUFTUyIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCmxpYnJhcnkoTUFTUykNCn0NCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCmxpYnJhcnkoZ2dwbG90MikNCn0NCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiY2FyZXQiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KbGlicmFyeShjYXJldCkNCn0NCmlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpDQpsaWJyYXJ5KHBhbmRlcikNCn0NCmlmICghcmVxdWlyZSgicmFuZG9tRm9yZXN0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCn0NCmlmICghcmVxdWlyZSgicnBhcnQiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicnBhcnQiKQ0KbGlicmFyeShycGFydCkNCn0NCmlmICghcmVxdWlyZSgicnBhcnQucGxvdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmxpYnJhcnkocnBhcnQucGxvdCkNCn0NCmlmICghcmVxdWlyZSgiaXByZWQiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiaXByZWQiKQ0KbGlicmFyeShpcHJlZCkNCn0NCmlmICghcmVxdWlyZSgibWxiZW5jaCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJtbGJlbmNoIikNCmxpYnJhcnkobWxiZW5jaCkNCn0NCmlmICghcmVxdWlyZSgicFJPQyIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwUk9DIikNCmxpYnJhcnkocFJPQykNCn0NCmlmICghcmVxdWlyZSgibmV1cmFsbmV0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm5ldXJhbG5ldCIpDQpsaWJyYXJ5KG5ldXJhbG5ldCkNCn0NCmlmICghcmVxdWlyZSgibmV1cmFsbmV0IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm5ldXJhbG5ldCIpDQpsaWJyYXJ5KG5ldXJhbG5ldCkNCn0NCmlmICghcmVxdWlyZSgiTmV1cmFsTmV0VG9vbHMiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiTmV1cmFsTmV0VG9vbHMiKQ0KbGlicmFyeShOZXVyYWxOZXRUb29scykNCn0NCiMjIA0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduPSdjZW50ZXInDQogICAgICAgICAgICAgICAgICAgICAgKSAgDQpgYGANCg0KDQpcDQoNCiMgSW50cm9kdWN0aW9uDQoNCioqTXVsdGlsYXllciBQZXJjZXB0cm9ucyAoTUxQcykqKiwgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgKipkZWVwIGxlYXJuaW5nIG1vZGVscyoqIG9yICAqKm11bHRpbGF5ZXIgbmV1cmFsIG5ldHdvcmtzKiosIGFyZSBhIHBvd2VyZnVsIGNsYXNzIG9mIGFsZ29yaXRobXMgd2l0aCB3aWRlIGFwcGxpY2F0aW9ucy4gVGhlc2UgbmV0d29ya3MgY29uc2lzdCBvZiBpbnRlcmNvbm5lY3RlZCBsYXllcnMgb2YgYXJ0aWZpY2lhbCAibmV1cm9ucywiIGVhY2ggcHJvY2Vzc2luZyBpbmZvcm1hdGlvbiBhbmQgcGFzc2luZyBpdCBmb3J3YXJkIHRvIHN1YnNlcXVlbnQgbGF5ZXJzLiBUaGUgZmlyc3QgbGF5ZXIgcmVjZWl2ZXMgcmF3IGlucHV0IGRhdGEsIHdoaWxlIGhpZGRlbiBsYXllcnMgdHJhbnNmb3JtIHRoaXMgZGF0YSB0aHJvdWdoIHdlaWdodGVkIGNvbm5lY3Rpb25zIGFuZCBub25saW5lYXIgZnVuY3Rpb25zLCBhbGxvd2luZyB0aGUgbmV0d29yayB0byBsZWFybiBjb21wbGV4IHBhdHRlcm5zLiBUaGUgZmluYWwgbGF5ZXIgcHJvZHVjZXMgcHJlZGljdGlvbnMgb3IgY2xhc3NpZmljYXRpb25zLg0KDQoNCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjkwJSJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvTUxQMC5wbmciKQ0KYGBgDQoNCg0KSW4gc3RhdGlzdGljYWwgbW9kZWxpbmcsIG11bHRpbGF5ZXIgbmV1cmFsIG5ldHdvcmtzIGNhbiBoYW5kbGUgaW50cmljYXRlIHJlbGF0aW9uc2hpcHMgaW4gZGF0YS4gVW5saWtlIGxpbmVhciByZWdyZXNzaW9uIG9yIHNpbXBsZSBkZWNpc2lvbiB0cmVlcywgIG11bHRpbGF5ZXIgbmV1cmFsIG5ldHdvcmsgbW9kZWxzIGNhbiBtb2RlbCBoaWdobHkgbm9ubGluZWFyIGludGVyYWN0aW9ucyBhbmQgYWRhcHQgdG8gbGFyZ2Utc2NhbGUsIGhpZ2gtZGltZW5zaW9uYWwgZGF0YXNldHMgd2hlcmUgY2xhc3NpYyBzdGF0aXN0aWNhbCBtb2RlbHMgb2Z0ZW4gc3RydWdnbGUuIFRoZWlyIGZsZXhpYmlsaXR5IGNvbWVzIGZyb20gdGhlaXIgYWJpbGl0eSB0byBpdGVyYXRpdmVseSBhZGp1c3Qgd2VpZ2h0cyBkdXJpbmcgdHJhaW5pbmcsIHJlZmluaW5nIHRoZWlyIHByZWRpY3Rpb25zIGFzIHRoZXkgZW5jb3VudGVyIG1vcmUgZGF0YS4NCg0KRGVzcGl0ZSB0aGVpciBzdHJlbmd0aHMsIG11bHRpbGF5ZXIgbmV1cmFsIG5ldHdvcmtzIHJlcXVpcmUgY2FyZWZ1bCB0dW5pbmcgYW5kIGxhcmdlIGRhdGFzZXRzIHRvIHBlcmZvcm0gd2VsbC4gDQoNCiogT3ZlcmZpdHRpbmfigJR3aGVyZSBhIG1vZGVsIG1lbW9yaXplcyB0cmFpbmluZyBkYXRhIGJ1dCBmYWlscyBvbiBuZXcgaW5wdXRz4oCUaXMgYSBjb21tb24gY2hhbGxlbmdlLCBtaXRpZ2F0ZWQgYnkgdGVjaG5pcXVlcyBsaWtlIGRyb3BvdXQgYW5kIHJlZ3VsYXJpemF0aW9uLiANCg0KKiBUaGVpciAqKmJsYWNrIGJveCoqIG5hdHVyZSBjYW4gYWxzbyBtYWtlIGludGVycHJldGluZyByZXN1bHRzIGRpZmZpY3VsdCwgcG9zaW5nIGNoYWxsZW5nZXMgaW4gZmllbGRzIHdoZXJlIGV4cGxhaW5hYmlsaXR5IGlzIGNydWNpYWwsIHN1Y2ggYXMgaGVhbHRoY2FyZSBvciBmaW5hbmNlLiANCg0KKipNTFAqKnMgYXJlIGEgZm91bmRhdGlvbmFsIGFyY2hpdGVjdHVyZSBpbiBtb2Rlcm4gbWFjaGluZSBsZWFybmluZyBhbmQgYXJ0aWZpY2lhbCBpbnRlbGxpZ2VuY2UuIFRoZWlyIGFiaWxpdHkgdG8gYXBwcm94aW1hdGUgY29tcGxleCwgbm9ubGluZWFyIGZ1bmN0aW9ucyBtYWtlcyB0aGVtIGhpZ2hseSB2ZXJzYXRpbGUgZm9yIGEgd2lkZSByYW5nZSBvZiByZWFsLXdvcmxkIHByb2JsZW1zLiANCg0KVGhlcmUgYXJlIHZhcmlvdXMgYWRhcHRhdGlvbnMgYW5kIGV4dGVuc2lvbnMgb2YgTUxQcywgZWFjaCBkZXZlbG9wZWQgZm9yIHNwZWNpZmljIHB1cnBvc2VzIGFjcm9zcyBkaWZmZXJlbnQgZmllbGRzLiBBIHNvbGlkIHVuZGVyc3RhbmRpbmcgb2YgTUxQcyBpcyBlc3NlbnRpYWwgZm9yIGdyYXNwaW5nIHRoZSB1bmRlcmx5aW5nIHByaW5jaXBsZXMgb2YgbWFueSBzcGVjaWFsaXplZCBuZXVyYWwgbmV0d29yayBhbGdvcml0aG1zLg0KDQpcDQoNCiMgQXJjaGl0ZWN0dXJlIG9mIE1MUA0KDQpBIE11bHRpbGF5ZXIgUGVyY2VwdHJvbiAoTUxQKSBpcyBnZW5lcmFsaXplZCBmcm9tIHRoZSBwZXJjZXB0cm9uIGJ5IGFkZGluZyBvbmUgb3IgbW9yZSBoaWRkZW4gbGF5ZXJzIGJldHdlZW4gdGhlIGlucHV0IGFuZCBvdXRwdXQgbGF5ZXJzLiBUaGF0IGlzLCBhbiBNTFAgaGFzIHRocmVlIG1ham9yIGNvbXBvbmVudHM6DQoNCi0gQW4gKippbnB1dCBsYXllcioqDQotIE9uZSBvciBtb3JlICoqaGlkZGVuIGxheWVycyoqIA0KLSBBbiAqKm91dHB1dCBsYXllcioqDQoNCkVhY2ggbGF5ZXIgY29uc2lzdHMgb2YgbXVsdGlwbGUgKipuZXVyb25zIChub2RlcykqKiBjb25uZWN0ZWQgdmlhIHdlaWdodHMsIHdpdGggbm9ubGluZWFyIGFjdGl2YXRpb24gZnVuY3Rpb25zIHRoYXQgY2FwdHVyZSBjb21wbGV4IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBmZWF0dXJlcyBhbmQgdGhlIHRhcmdldCwgdGhlcmVieSBpbXByb3ZpbmcgdGhlIGFsZ29yaXRobSdzIHBlcmZvcm1hbmNlLg0KDQpJZiBhbiBNTFAgaGFzIG9ubHkgb25lIGhpZGRlbiBsYXllciwgdGhpcyBNTFAgaXMgdXN1YWxseSBjYWxsZWQgKipzaGFsbG93IG5ldXJhbCBuZXR3b3JrKiouICoqRGVlcCBuZXVyYWwgbmV0d29ya3MqKiBoYXZlIHR3byBvciBtb3JlIGhpZGRlbiBsYXllcnMuDQoNCkluIHRoZSBzdWJzZXF1ZW50IHNlY3Rpb25zLCB3ZSB3aWxsIGJyaWVmbHkgaW50cm9kdWNlIHRoZSB0aGVvcnkgb2YgbmV1cmFsIG5ldHdvcmtzIHdpdGggYW4gZW1waGFzaXMgb24gc2hhbGxvdyBuZXR3b3Jrcy4gU29tZSBjYXNlIHN0dWRpZXMgd2lsbCBhbHNvIGJlIGluY2x1ZGVkIHRvIGRlbW9uc3RyYXRlIHRoZSBhcHBsaWNhdGlvbiBvZiBuZXVyYWwgbmV0d29yayBtb2RlbHMgaW4gcmVncmVzc2lvbiBhbmQgY2xhc3NpZmljYXRpb24uDQoNCg0KDQojIyBNYXRoZW1hdGljcyBvZiBNTFANCg0KRm9yIGlsbHVzdHJhdGlvbiwgd2UgdXNlIHRoZSBmb2xsb3dpbmcgb25lLWhpZGRlbi1sYXllciBNTFAgcmVncmVzc2lvbiB0byBleHBsYWluIHRoZSBtYXRoZW1hdGljYWwgZm91bmRhdGlvbnMgb2YgdGhlIGFsZ29yaXRobS4gVGhlIG9iamVjdGl2ZSBpcyB0byBwcmVkaWN0IHRoZSB0YXJnZXQgdmFyaWFibGUgDQokeSQgdXNpbmcgaW5wdXQgZmVhdHVyZXMgJFx7eF8xLCB4XzIsIFxjZG90cywgeF9uIFx9JC4gSWYgYWN0aXZhdGlvbiBmdW5jdGlvbnMgJFxwaGlfMShcY2RvdCkkIGFuZCAkXHBoaShcY2RvdCkkIGFyZSBjb3JyZWN0bHkgc3BlY2lmaWVkLCB0aGVuIHRoZSB0YXJnZXQgY2FuIGJlIGV4cHJlc3NlZCBhcyBhIGNvbXBvc2l0aW9uIG9mIHdlaWdodHMgdGhyb3VnaCBmdW5jdGlvbiBjb21wb3NpdGlvbi4NCg0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5MCUifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL01MUDAxLnBuZyIpDQpgYGANCg0KSW4gZXNzZW5jZSwgTUxQIGlzIGEgcHJvYmxlbSBvZiBhcHByb3hpbWF0aW5nIGEgbXVsdGktdmFyaWFibGUgZnVuY3Rpb24uIEFzc3VtZSB0aGF0IHRoZXJlIGFyZSAkbiQgZmVhdHVyZXMgaW4gdGhlIHNjZW5hcmlvIG9mIHRoZSBhYm92ZSBvbmUtaGlkZGVuLWxheWVyIE1MUCwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0YXJnZXQgdmFyaWFibGUgJHkkIGlzIGNoYXJhY3Rlcml6ZWQgYnkgJHkgPSBmKHhfMSwgeF8yLCBcY2RvdHMsIHhfbjogXG1hdGhiZnt3fSkkLCB3aGVyZSAkZihcY2RvdCkkIGlzIHVua25vd24gYW5kICRcbWF0aGJme3d9JCBpcyB0aGUgdmVjdG9yIG9mIHVua25vd24gcGFyYW1ldGVycy4gVGhhdCBpcywgaW4gdGhlIGFib3ZlIE1MUCwgdGhlIHRhcmdldCBjYW4gYmUgZXhwcmVzc2VkIGFzDQoNCiQkDQp5ID0gZih4XzEsIHhfMiwgXGNkb3RzLCB4X246IFxtYXRoYmZ7d30pID0gXHBoaV8yW1xwaGlfMSh4XzEsIHhfMiwgXGNkb3RzLCB4X246IFxtYXRoYmZ7d30pXS4NCiQkDQoNCjxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfU5vdGUgdGhhdCAkZihcY2RvdCkkIGlzIGFuIGFyYml0cmFyeSBmdW5jdGlvbiAkZjogXG1hdGhiYntSfV5uIFxyaWdodGFycm93IFxtYXRoYmJ7Un0kLioqPC9mb250PiBGb3IgYSBnaXZlbiBkYXRhIHNldCAkXHsoeF97MWl9LCB4X3syaX0sIFxjZG90cywgeF97bml9KVx9X3tpPTF9Xm4kIGFuZCBlc3RpbWF0ZWQgJFxoYXR7XG1hdGhiZnt3fX0kLCBkZXBlbmRpbmcgb24gYXBwbGljYXRpb25zLCB0aGUgbG9zcyBmdW5jdGlvbiBpcyBkZWZpbmVkIHJlc3BlY3RpdmVseSBieQ0KDQoqICoqQ29udGludW91cyBSZWdyZXNzaW9uIChNU0UpKioNCg0KJCQNCiBcbWF0aGNhbHtMfSA9IFxmcmFjezF9e059IFxzdW1fe2M9MX1eQyAoeV9pIC0gXGhhdHt5fV9pKV4yIA0KJCQNCg0KKiAqKkRpc2NyZXRlIENsYXNzaWZpY2F0aW9uIChDcm9zcy1lbnRyb3B5KSoqDQoNCiQkDQogXG1hdGhjYWx7TH0gPSAtIFxzdW1fe2M9MX1eQyAgeV9jIFxsb2coXGhhdHt5fV9jKQ0KJCQNCmZvciAkQyQgY2xhc3Nlcy4gVXNpbmcgdGhlIGdyYWRpZW50IGRlY2VudCBtZXRob2QsIHdlIGNhbiBleHByZXNzIHRoZSB3ZWlnaHQgdXBkYXRpbmcgZm9ybXVsYSBpbiB0aGUgZm9sbG93aW5nDQoNCiQkDQpcbWF0aGJme1d9XnsoaisxKX0gXGxlZnRhcnJvdyBcbWF0aGJme1d9Xnsoail9IC0gXGV0YSBcZnJhY3tccGFydGlhbCBcbWF0aGNhbHtMfX17XHBhcnRpYWwgXG1hdGhiZntXfX1cQmlnfF97XG1hdGhiZntXfT1cbWF0aGJme1d9Xnsoail9fQ0KJCQNCndoZXJlICRcZXRhJCBpcyB0aGUgaHlwZXJwYXJhbWV0ZXIgb2YgKipsZWFybmluZyByYXRlKiouICANCg0KDQojIyBVbml2ZXJzYWwgQXBwcm94aW1hdGlvbiBUaGVvcmVtDQoNClRoZSAqKlVuaXZlcnNhbCBBcHByb3hpbWF0aW9uIFRoZW9yZW0qKiBpcyBhIGZ1bmRhbWVudGFsIHJlc3VsdCBpbiB0aGUgdGhlb3J5IG9mIGFydGlmaWNpYWwgbmV1cmFsIG5ldHdvcmtzLCBzdGF0aW5nIHRoYXQgYSBmZWVkZm9yd2FyZCBuZXVyYWwgbmV0d29yayB3aXRoIGEgc2luZ2xlIGhpZGRlbiBsYXllciBjb250YWluaW5nIGEgZmluaXRlIG51bWJlciBvZiBuZXVyb25zIGNhbiBhcHByb3hpbWF0ZSBjb250aW51b3VzIGZ1bmN0aW9ucyBvbiBjb21wYWN0IHN1YnNldHMgb2YgJFxtYXRoYmJ7Un1ebiQgdW5kZXIgbWlsZCBjb25kaXRpb25zIG9uIHRoZSBhY3RpdmF0aW9uIGZ1bmN0aW9uLiBUaGUgZm9ybWFsIHN0YXRlbWVudCBpcyBnaXZlbiBpbiB0aGUgZm9sbG93aW5nOiAgICANCg0KKipDeWJlbmtvJ3MgVGhlb3JlbSAoMTk4OSkqKjogTGV0ICRccGhpKFxjZG90KSQgYmUgYSBjb250aW51b3VzLCBub24tY29uc3RhbnQgYWN0aXZhdGlvbiBmdW5jdGlvbiAoZS5nLiwgc2lnbW9pZCkuIEdpdmVuIGFueSBjb250aW51b3VzIGZ1bmN0aW9uICRmOiBbMCwxXV5uIFxyaWdodGFycm93IFxtYXRoYmJ7Un0kIGFuZCAkXGVwc2lsb24gPiAwJCwgdGhlcmUgZXhpc3RzIGEgKipzaW5nbGUtaGlkZGVuLWxheWVyIE1MUCoqIChtdWx0aWxheWVyIHBlcmNlcHRyb24pIHdpdGggZmluaXRlbHkgbWFueSBoaWRkZW4gbm9kZXMgKG5ldXJvbnMpLCBkZW5vdGVkIGJ5ICROKFxtYXRoYmZ7eH0pJCwgc3VjaCB0aGF0IA0KDQokJA0KXHN1cF97XG1hdGhiZnt4fVxpbiBbMCwxXV5ufSBcYmlnfGYoXG1hdGhiZnt4fSkgLSBOKFxtYXRoYmZ7eH0pIFxiaWd8IDwgXGVwc2lsb24uDQokJA0KDQpUaGUgYWJvdmUgYW5hbHlzaXMgYXNzdW1lcyB0aGUgc3VwcG9ydCBvZiB0aGUgbXVsdGl2YXJpYWJsZSBmdW5jdGlvbiBpcyAkWzAsMV1ebiQuIFRoaXMgY2FuIGJlIGVhc2lseSBnZW5lcmFsaXplZCB0byBhbnkgZnVuY3Rpb24gd2l0aCBnZW5lcmFsIGZpbml0ZSBzdXBwb3J0ICRbYS0xLCBiXzFdXHRpbWVzIFthXzIsIGJfMl0gXHRpbWVzIFxjZG90c1x0aW1lc1thX2ssIGJfa10kIHZpYSBhIGxpbmVhciB0cmFuc2Zvcm1hdGlvbi4gRm9yIGV4YW1wbGUsIGFzc3VtZSAkZl9rKHhfaykkIGhhcyBzdXBwb3J0ICRbYV9rLCBiX2tdJCwgd2UgY2FuIHVzZSBsaW5lYXIgdHJhbnNmb3JtYXRpb24gJHlfayA9ICh4X2sgLSBhX2spLyhiX2sgLSBhX2spJCB3aGljaCBpbXBsaWVzIHRoYXQgJHlfayBcaW4gWzAsMV0kLiANCg0KVGhpcyBleHBsYWlucyB3aHkgYSAqKm9uZS1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbioqIGNhbiBhcHByb3hpbWF0ZSBhbnkgY29udGludW91cyBmdW5jdGlvbiBkZWZpbmVkIG9uIGEgZmluaXRlIHN1cHBvcnQgdXNpbmcgZmluaXRlbHkgbWFueSBub2RlcyBpbiB0aGUgaGlkZGVuIGxheWVyLiBGb3IgZnVuY3Rpb25zIHdpdGggaW5maW5pdGUgc3VwcG9ydCwgd2UgY2FuIGZpcnN0IGFwcHJveGltYXRlIHRoZW0gb24gYSBmaW5pdGUgZG9tYWluIGFuZCB0aGVuIGFwcGx5IHRoZSAqKnVuaXZlcnNhbCBhcHByb3hpbWF0aW9uIHRoZW9yZW0qKiB0byBhY2hpZXZlIHRoZSBkZXNpcmVkIHJlc3VsdC4NCg0KDQpSZWNhbGwgdGhhdCwgdGhlIG9iamVjdGl2ZSBvZiByZWdyZXNzaW9uIGFuYWx5c2lzIGlzIHRvIGVzdGltYXRlIHRoZSAqKnVua25vd24qKiByZWdyZXNzaW9uIGZ1bmN0aW9uIGJhc2VkIG9uIGEgc2FtcGxlIHRha2VuIGZyb20gdGhlIHVuZGVybHlpbmcgcG9wdWxhdGlvbiBhbmQgdGhlbiB1c2UgdGhlICoqZXN0aW1hdGVkKiogcmVncmVzc2lvbiBmdW5jdGlvbiB0byANCg0KKiBhc3Nlc3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGZlYXR1cmUgdmFyaWFibGVzIGFuZCB0aGUgdGFyZ2V0IHZhcmlhYmxlOw0KKiBwcmVkaWN0IHRoZSB2YWx1ZSBvZiB0aGUgdGFyZ2V0IHZhcmlhYmxlIGJhc2VkIG9uIHRoZSB2YWx1ZXMgb2YgdGhlIGlucHV0IGZlYXR1cmUgdmFyaWFibGVzLg0KDQpUaGUgKip1bml2ZXJzYWwgYXBwcm94aW1hdGlvbiB0aGVvcmVtKiogZm9yIHNoYWxsb3cgbmV0d29ya3MgZ3VhcmFudGVlcyB0aGVpciBjYXBhYmlsaXR5IGFzIGEgKipmdW5jdGlvbiBhcHByb3hpbWF0b3IqKiwgbWFraW5nIGl0IGEgbmF0dXJhbCB0aGVvcmV0aWNhbCBmb3VuZGF0aW9uIGZvciBuZXVyYWwgbmV0d29yayByZWdyZXNzaW9uIG1ldGhvZHMuDQoNCg0KDQojIyBTb21lIEdyYXBoaWNhbCBEZW1vbnN0cmF0aW9ucw0KDQpUaGlzIHN1YnNlY3Rpb24gdXNlcyBhIHNpbXVsYXRpb24gYXBwcm9hY2ggdG8gZGVtb25zdHJhdGUgd2h5IE1MUCBpcyBhbHNvIGEgbm9uLWNsYXNzaWNhbCBmYW1pbHkgb2YgYWxnb3JpdGhtaWMgcmVncmVzc2lvbiBtb2RlbHMuIFdlIHRha2UgYSBzZXQgb2YgcG9pbnRzIG9uIHRoZSBzdXJmYWNlIG9mIGEgaHlwb3RoZXNpemVkIGVsbGlwdGljIHBhcmFib2xvaWQgcmVncmVzc2lvbiBzdXJmYWNlIGFuZCB0aGVuIHVzZSBNTFAgdG8gZXN0aW1hdGUgdGhlIGh5cG90aGV0aWNhbCBlbGxpcHRpYyBwYXJhYm9sb2lkIG1vZGVsLiBUaGUgZWxsaXB0aWMgcGFyYWJvbG9pZCB1c2VkIHRvIGdlbmVyYXRlIGRhdGEgaXMgZ2l2ZW4gYnkNCg0KJCQNCnogPSB4XzFeMiArIHhfMl4yDQokJA0KVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgdGhlIGh5cG90aGV0aWNhbCByZWdyZXNzaW9uIHN1cmZhY2UsIGFsb25nIHdpdGggdGhlIGRhdGEgcG9pbnRzIHVzZWQgdG8gZXN0aW1hdGUgaXQuDQoNCg0KYGBge3J9DQojbGlicmFyeShwbG90bHkpDQoNCiMgRGVmaW5lIHRoZSBlbGxpcHRpYyBwYXJhYm9sb2lkIGZ1bmN0aW9uOiB6ID0gKHheMiAvIGFeMikgKyAoeV4yIC8gYl4yKQ0KIyBDcmVhdGUgYSBncmlkIG9mIHggYW5kIHkgdmFsdWVzDQp4IDwtIHNlcSgtMiwgMiwgbGVuZ3RoID0yMDApDQp5IDwtIHNlcSgtMiwgMiwgbGVuZ3RoID0yMDApDQp6IDwtIG91dGVyKHgsIHksIGZ1bmN0aW9uKHgsIHkpICh4XjIgICsgeV4yICkpDQoNCiMjIHNhbXBsZSBwb2ludHMgb24gdGhlIHN1cmZhY2UgdG8gZGVmaW5lIGEgZGF0YSBzZXQNCnguc2ltIDwtIHNhbXBsZSh4KQ0KeS5zaW0gPC0gc2FtcGxlKHkpDQp6LnNhbXBsZSA8LSAoeC5zaW1eMiArIHkuc2ltXjIpICsgcm5vcm0oMjAwLCAwLCAwLjUpDQoNCiMgUGxvdCBtYXJnaW5zDQptIDwtIGxpc3QoIGwgPSA1MCwgciA9IDUwLCBiID0gMTAwLCB0ID0gMTAwLCBwYWQgPSA0KQ0KDQojIENyZWF0ZSB0aGUgM0Qgc3VyZmFjZSBwbG90DQpwbG90X2x5KHggPSB4LCB5ID0geSwgeiA9IHosIHR5cGUgPSAic3VyZmFjZSIpICU+JSAgIyB0aGUgc3VyZmFjZQ0KICBhZGRfbWFya2Vycyh4ID0geC5zaW0sICAgIyBzYW1wbGUgcG9pbnRzIG5lYXIgdGhlIHN1cmZhY2UNCiAgICAgICAgICAgICAgeSA9IHkuc2ltLCANCiAgICAgICAgICAgICAgeiA9IHouc2FtcGxlLCAgIA0KICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAzLCBjb2xvciA9ICJkYXJrcmVkIikpICU+JQ0KICBsYXlvdXQobWFyZ2luID0gbSwgDQogICAgICAgICAgdGl0bGUgPSAiRWxsaXB0aWMgUGFyYWJvbG9pZDogSHlwb3RoZXNpemVkIE1vZGVsIGZvciBEYXRhIEdlbmVyYXRpb24iLA0KICAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiWCIpLA0KICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiWSIpLA0KICAgICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAiWiIpDQogICAgICAgICAgICAgICAgICAgKQ0KICAgICAgICApDQpgYGANCg0KDQpJbiBwcmFjdGljZSwgd2Ugb25seSBvYnNlcnZlIGRhdGEgcG9pbnRzIGZyb20gYW4gdW5rbm93biBzdXJmYWNlLiBXZSB0aGVyZWZvcmUgcmVtb3ZlIHRoZSBoeXBvdGhldGljYWwgcmVncmVzc2lvbiBzdXJmYWNlIGFuZCB2aXN1YWxpemUganVzdCB0aGUgZGF0YSBwb2ludHMgdG8gcmV2ZWFsIHRoZSBkYXRhIGNsb3VkJ3Mgc3RydWN0dXJlLg0KDQoNCmBgYHtyfQ0KcGxvdF9seSgpICU+JQ0KICBhZGRfbWFya2Vycyh4ID0geC5zaW0sIHkgPSB5LnNpbSwgeiA9IHouc2FtcGxlLCANCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpKSAlPiUNCiAgbGF5b3V0KCBtYXJnaW4gPSBtLA0KICAgICAgICAgICB0aXRsZSA9ICJTYW1wbGUgcG9pbnRzIGZyb20gdGhlIGVsbGlwdGljIHBhcmFib2xvaWQiLA0KICAgICAgICAgICBzY2VuZSA9IGxpc3QoIHhheGlzID0gbGlzdCh0aXRsZSA9ICJYIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAiWiIpDQogICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICApDQpgYGANCg0KVGhlcmUgaXMgYSBub25saW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRhcmdldCB2YXJpYWJsZSAoJHokKSBhbmQgZmVhdHVyZSB2YXJpYWJsZXMgKCR4XzEkIGFuZCAkeF8yJCkuIEluIHRoZSBmb2xsb3dpbmcgZmlndXJlcywgd2UgY29tcGFyZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhyZWUgbW9kZWxzIGZpdHRlZCB0byB0aGUgc2FtZSBkYXRhc2V0OiBhIHNpbmdsZSBwZXJjZXB0cm9uLCBhIG9uZS1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiwgYW5kIGEgdHdvLWhpZGRlbi1sYXllciBwZXJjZXB0cm9uLiBXZSB0aGVuIHBsb3QgdGhlaXIgY29ycmVzcG9uZGluZyBlc3RpbWF0ZWQgcmVncmVzc2lvbiBzdXJmYWNlcyB0byB2aXN1YWxpemUgdGhlIGdvb2RuZXNzIG9mIGZpdC4NCg0KDQpgYGB7cn0NCiNsaWJyYXJ5KG5ldXJhbG5ldCkNCiNsaWJyYXJ5KGdncGxvdDIpDQojbGlicmFyeShwbG90bHkpICAjIEZvciAzRCB2aXN1YWxpemF0aW9uDQoNCiMgR2VuZXJhdGUgdHJhaW5pbmcgZGF0YQ0KI3NldC5zZWVkKDEyMykNCiN4IDwtIHNlcSgtMiwgMiwgbGVuZ3RoID0yMCkNCiN5IDwtIHNlcSgtMiwgMiwgbGVuZ3RoID0yMCkNCiN6X3RyYWluIDwtIHhfdHJhaW5eMiArIHlfdHJhaW5eMiAjICsgcm5vcm0oNTAwLCBzZCA9IDAuMSkgICMgQWRkZWQgbm9pc2UNCg0KIyMgVGhlIHdvcmtpbmcgZGF0YSBzZXQ6IHdlIGNhbGwgaXQgdGhlIHRyYWluaW5nIGRhdGEgc2V0DQp0cmFpbi5kYXRhIDwtIGRhdGEuZnJhbWUoeCA9IHguc2ltLCB5ID0geS5zaW0sIHogPSB6LnNhbXBsZSkNCg0KIyMjIG5vIGhpZGRlbiBsYXllcg0KaGlkZGVuMC4wIDwtIG5ldXJhbG5ldCh6IH4geCArIHksIA0KICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4uZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IGMoMCksIA0KICAgICAgICAgICAgICAgICAgICAgICBsaW5lYXIub3V0cHV0ID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgc3RlcG1heCA9IDFlNikgICMgSW5jcmVhc2UgaXRlcmF0aW9ucw0KDQoNCg0KIyMjIHNpbmdsZSBoaWRkZW4gbGF5ZXINCmhpZGRlbjEuOCA8LSBuZXVyYWxuZXQoeiB+IHggKyB5LCANCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICBoaWRkZW4gPSBjKDgpLCANCiAgICAgICAgICAgICAgICAgICAgICAgbGluZWFyLm91dHB1dCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgIHN0ZXBtYXggPSAxZTYpICAjIEluY3JlYXNlIGl0ZXJhdGlvbnMNCg0KDQoNCiMgVHJhaW4gbmV1cmFsIG5ldHdvcmsgd2l0aCAyIGhpZGRlbiBsYXllcnMgKDggYW5kIDQgbmV1cm9ucykNCmhpZGRlbjIuOC40IDwtIG5ldXJhbG5ldCh6IH4geCArIHksIA0KICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4uZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IGMoOCw0KSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxpbmVhci5vdXRwdXQgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICBzdGVwbWF4ID0gMWU2KSAgIyBJbmNyZWFzZSBpdGVyYXRpb25zDQoNCiMgQ3JlYXRlIHRlc3QgZ3JpZCB0byBkcmF3IHRoZSBQcmVkaWN0ZWQgc3VyZmFjZSBiYXNlZCBvbiBkaWZmZXJlbnQgTk4gbW9kZWxzDQp4LnRlc3QgPC0gc2VxKC0yLCAyLCBsZW5ndGgub3V0ID0gMjAwKQ0KeS50ZXN0IDwtIHNlcSgtMiwgMiwgbGVuZ3RoLm91dCA9IDIwMCkNCnRlc3QuZ3JpZCA8LSBleHBhbmQuZ3JpZCh4ID0geC50ZXN0LCB5ID0geS50ZXN0KQ0Kei50ZXN0MC4wIDwtIG1hdHJpeChwcmVkaWN0KGhpZGRlbjAuMCwgdGVzdC5ncmlkKSxuY29sPTIwMCkNCnoudGVzdDEuOCA8LSBtYXRyaXgocHJlZGljdChoaWRkZW4xLjgsIHRlc3QuZ3JpZCksbmNvbD0yMDApDQp6LnRlc3QyLjguNCA8LSBtYXRyaXgocHJlZGljdChoaWRkZW4yLjguNCwgdGVzdC5ncmlkKSxuY29sPTIwMCkNCg0KYGBgDQoNClRoZSBmb2xsb3dpbmcgZmlndXJlIHNob3dzIHRoZSBwcmVkaWN0ZWQgc3VyZmFjZSBmcm9tIHRoZSBwZXJjZXB0cm9uIG1vZGVsLCB3aGljaCBwcm9kdWNlcyBhIGxpbmVhciBoeXBlcnBsYW5lLCBhbG9uZyB3aXRoIHRoZSBkYXRhIHBvaW50cyB1c2VkIHRvIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIHN1cmZhY2UuIFRoZSBwZXJjZXB0cm9uIGZpdHMgdGhlIGRhdGEgcG9vcmx5Lg0KDQoNCmBgYHtyfQ0KIyMgc2V0IHVwIHBsb3QgbWFyZ2luDQptIDwtIGxpc3QobCA9IDUwLHIgPSA1MCwgYiA9IDEwMCwgdCA9IDEwMCwgcGFkID0gNCkNCg0KIyAzRCBWaXN1YWxpemF0aW9uIHdpdGggcGxvdGx5DQpwbG90X2x5KHggPSB+eC50ZXN0LCB5ID0gfnkudGVzdCwgeiA9IH56LnRlc3QwLjAsIHR5cGUgPSAic3VyZmFjZSIpICU+JQ0KICBhZGRfbWFya2VycyhkYXRhID0gdHJhaW4uZGF0YSwgDQogICAgICAgICAgICAgIHggPSB+eCwgeSA9IH55LCB6ID0gfnosIA0KICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAzLCBjb2xvciA9ICJkYXJrcmVkIikpICU+JQ0KICBsYXlvdXQodGl0bGUgPSAnUGVyY2VwdHJvbiBNb2RlbCAod2l0aCBubyBoaWRkZW4gbGF5ZXIpJywNCiAgICAgICAgIGF1dG9zaXplID0gVCwgDQogICAgICAgICAjd2lkdGggPSA1MDAsIA0KICAgICAgICAgI2hlaWdodCA9IDUwMCwNCiAgICAgICAgIG1hcmdpbiA9IG0sIA0KICAgICAgICAgc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJYIiksDQogICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiWSIpLA0KICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIloiKSkpDQpgYGANCg0KDQpOZXh0LCB3ZSBmaXQgYSAqKm9uZS1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiB3aXRoIDggbm9kZXMqKiB0byB0aGUgc2FtZSBkYXRhc2V0LiBUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyBib3RoIHRoZSBlc3RpbWF0ZWQgcmVncmVzc2lvbiBzdXJmYWNlIGFuZCB0aGUgZGF0YSBwb2ludHMuIFdlIG9ic2VydmUgdGhhdCB0aGUgcHJlZGljdGVkIHN1cmZhY2UgY2xvc2VseSBhcHByb3hpbWF0ZXMgdGhlICoqaHlwb3RoZXNpemVkIHJlZ3Jlc3Npb24gc3VyZmFjZSoqLiBOb3RlIHRoYXQgdGhlIG51bWJlciBvZiBub2RlcyBpbiB0aGUgaGlkZGVuIGxheWVyIGlzIGEgdHVuYWJsZSBoeXBlcnBhcmFtZXRlciB0aGF0IGNhbiBiZSBvcHRpbWl6ZWQgdG8gaW1wcm92ZSBtb2RlbCBwZXJmb3JtYW5jZS4NCg0KDQpgYGB7cn0NCiMgM0QgVmlzdWFsaXphdGlvbiB3aXRoIHBsb3RseQ0KcGxvdF9seSh4ID0gfngudGVzdCwgeSA9IH55LnRlc3QsIHogPSB+ei50ZXN0MS44LCB0eXBlID0gInN1cmZhY2UiKSAlPiUNCiAgYWRkX21hcmtlcnMoZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICB4ID0gfngsIHkgPSB+eSwgeiA9IH56LCANCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gJ0VzdGltYXRlZCBPbmUtaGlkZGVuLWxheWVyIFBlcmNlcHRyb24gKHdpdGggOCBub2RlcyknLA0KICAgICAgICAgYXV0b3NpemUgPSBULCANCiAgICAgICAgICN3aWR0aCA9IDUwMCwgDQogICAgICAgICAjaGVpZ2h0ID0gNTAwLA0KICAgICAgICAgbWFyZ2luID0gbSwgDQogICAgICAgICBzY2VuZSA9IGxpc3QoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlgiKSwNCiAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJZIiksDQogICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAiWiIpKSkNCmBgYA0KDQpGaW5hbGx5LCB3ZSBleGFtaW5lIGEgdHdvLWhpZGRlbi1sYXllciBwZXJjZXB0cm9uIGFyY2hpdGVjdHVyZSwgd2l0aCA4IG5vZGVzIGluIHRoZSBmaXJzdCBoaWRkZW4gbGF5ZXIgYW5kIDQgbm9kZXMgaW4gdGhlIHNlY29uZCBoaWRkZW4gbGF5ZXIsIHRyYWluZWQgb24gdGhlIHNhbWUgZGF0YSBzZXQuDQoNCg0KYGBge3J9DQojIDNEIFZpc3VhbGl6YXRpb24gd2l0aCBwbG90bHkNCnBsb3RfbHkoeCA9IH54LnRlc3QsIHkgPSB+eS50ZXN0LCB6ID0gfnoudGVzdDIuOC40LCB0eXBlID0gInN1cmZhY2UiKSAlPiUNCiAgYWRkX21hcmtlcnMoZGF0YSA9IHRyYWluLmRhdGEsIA0KICAgICAgICAgICAgICB4ID0gfngsIHkgPSB+eSwgeiA9IH56LCANCiAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMywgY29sb3IgPSAiZGFya3JlZCIpKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gJ0VzdGltYXRlZCBUd28taGlkZGVuLWxheWVyIFBlcmNlcHRyb24gKHdpdGggOCBhbmQgNCBub2RlcyknLA0KICAgICAgICAgYXV0b3NpemUgPSBULCANCiAgICAgICAgICN3aWR0aCA9IDUwMCwgDQogICAgICAgICAjaGVpZ2h0ID0gNTAwLA0KICAgICAgICAgbWFyZ2luID0gbSwgDQogICAgICAgICBzY2VuZSA9IGxpc3QoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlgiKSwNCiAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJZIiksDQogICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAiWiIpKSkNCmBgYA0KDQpUaGUgZmlndXJlIGFib3ZlIHNob3dzIGJvdGggdGhlIGVzdGltYXRlZCBwcmVkaWN0aW9uIHN1cmZhY2UgYW5kIHRoZSBvcmlnaW5hbCBkYXRhIHBvaW50cy4gV2Ugb2JzZXJ2ZSBhIHNpZ25pZmljYW50IGRpc2NyZXBhbmN5IGJldHdlZW4gdGhlICoqZXN0aW1hdGVkIHJlZ3Jlc3Npb24gc3VyZmFjZSoqIGFuZCB0aGUgKipoeXBvdGhlc2l6ZWQgKHRydWUpIHJlZ3Jlc3Npb24gc3VyZmFjZSoqLiBVcG9uIGNsb3NlciBleGFtaW5hdGlvbiwgd2UgZmluZCB0aGF0IHRoZSBlc3RpbWF0ZWQgcmVncmVzc2lvbiBzdXJmYWNlIHlpZWxkcyBzbWFsbCByZXNpZHVhbHMsIGFzIHRoZSBkYXRhIHBvaW50cyBsaWUgY2xvc2UgdG8gdGhlIGZpdHRlZCBzdXJmYWNlLiBUaGlzIHN1Z2dlc3RzIHBvdGVudGlhbCAqKm92ZXJmaXR0aW5nKiogaW4gdGhlIHR3by1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiBtb2RlbC4NCg0KDQojIE1MUCBNb2RlbGluZyBQcm9jZXNzIA0KDQpNTFBzIGNhbiBsZWFybiBjb21wbGV4LCBub24tbGluZWFyIHJlbGF0aW9uc2hpcHMgZHVlIHRvIHRoZWlyIG11bHRpcGxlIGxheWVycyBvZiBpbnRlcmNvbm5lY3RlZCBuZXVyb25zLiBIb3dldmVyLCBkZXNpZ25pbmcgYW5kIHRyYWluaW5nIGFuIGVmZmVjdGl2ZSBNTFAgcmVxdWlyZXMgY2FyZWZ1bCBjb25zaWRlcmF0aW9uIG9mIGRhdGEgcHJlcHJvY2Vzc2luZywgYXJjaGl0ZWN0dXJlIHNlbGVjdGlvbiwgb3B0aW1pemF0aW9uIHRlY2huaXF1ZXMsIGFuZCBvdGhlciBzdHJhdGVnaWVzLg0KDQojIyBEYXRhIFByZXByb2Nlc3NpbmcNCg0KQmVmb3JlIHRyYWluaW5nIGFuIE1MUCwgdGhlIGlucHV0IGRhdGEgbXVzdCBiZSBwcm9wZXJseSBwcmVwYXJlZCB0byBtYWtlIHN1cmUgdGhlIE1MUCBsZWFybnMgZWZmaWNpZW50bHkuDQoNCiMjIyBGZWF0dXJlIFNjYWxpbmcNCg0KTUxQcyBhcmUgc2Vuc2l0aXZlIHRvIGZlYXR1cmUgc2NhbGVzLCBzbyBub3JtYWxpemF0aW9uIChlLmcuLCBNaW4tTWF4IHNjYWxpbmcpIG9yIHN0YW5kYXJkaXphdGlvbiAoZS5nLiwgWi1zY29yZSBub3JtYWxpemF0aW9uKSBpcyB0eXBpY2FsbHkgYXBwbGllZCB0byBlbnN1cmUgc3RhYmxlIGdyYWRpZW50IGRlc2NlbnQgb3B0aW1pemF0aW9uLiANCg0KV2hldGhlciB0aGUgdGFyZ2V0IHZhcmlhYmxlIG11c3QgYmUgcmVzY2FsZWQgaW4gYSBuZXVyYWwgbmV0d29yayBkZXBlbmRzIG9uIHRoZSBjb250ZXh0LCBidXQgaXQgaXMgb2Z0ZW4gc3Ryb25nbHkgcmVjb21tZW5kZWQgZm9yIG9wdGltYWwgcGVyZm9ybWFuY2UuDQoNClRoZSBzY2FsaW5nIG9mIHRoZSB0YXJnZXQgaXMgbmVjZXNzYXJ5IHdoZW4NCg0KKiAqKk91dHB1dCBBY3RpdmF0aW9uIENvbnN0cmFpbnRzKio6IElmIHVzaW5nIGEgc2lnbW9pZCAoYm91bmRlZCB0byBbMCwgMV0pIG9yIHRhbmggKGJvdW5kZWQgdG8gWy0xLCAxXSkgb3V0cHV0IGFjdGl2YXRpb24sIHRoZSB0YXJnZXQgdmFyaWFibGUgbXVzdCBiZSBzY2FsZWQgdG8gbWF0Y2ggdGhlc2UgcmFuZ2VzLg0KDQoqICoqTG9zcyBGdW5jdGlvbiBTZW5zaXRpdml0eSoqOiBMb3NzIGZ1bmN0aW9ucyBsaWtlIE1TRSAoTWVhbiBTcXVhcmVkIEVycm9yKSBhcmUgc2Vuc2l0aXZlIHRvIHNjYWxlLiBMYXJnZSB0YXJnZXQgdmFsdWVzIGNhbiBsZWFkIHRvIHVuc3RhYmxlIGdyYWRpZW50cyBvciBzbG93IGNvbnZlcmdlbmNlLg0KDQoqICoqUmVndWxhcml6YXRpb24gVGVybXMqKjogJExfMSQvJExfMiQgcmVndWxhcml6YXRpb24gcGVuYWxpemVzIGxhcmdlIHdlaWdodHMuIFVuc2NhbGVkIHRhcmdldHMgbWF5IGZvcmNlIHRoZSBuZXR3b3JrIHRvIGxlYXJuIGRpc3Byb3BvcnRpb25hdGVseSBsYXJnZSB3ZWlnaHRzIHRvIGNvbXBlbnNhdGUuDQoNCldoaWxlIG5vdCBhbHdheXMgbWFuZGF0b3J5LCAqKnJlc2NhbGluZyB0aGUgdGFyZ2V0IHZhcmlhYmxlKiogdHlwaWNhbGx5ICoqaW1wcm92ZXMqKiB0cmFpbmluZyBzdGFiaWxpdHksIHNwZWVkLCBhbmQgbW9kZWwgcGVyZm9ybWFuY2XigJRlc3BlY2lhbGx5IGZvciBib3VuZGVkIGFjdGl2YXRpb25zIG9yIGxhcmdlLXZhbHVlIHJhbmdlcy4gQWx3YXlzIHZhbGlkYXRlIHdpdGggeW91ciBzcGVjaWZpYyBhcmNoaXRlY3R1cmUgYW5kIGRhdGEuDQoNCg0KDQojIyMgSGFuZGxpbmcgTWlzc2luZyBEYXRhDQoNCkhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzIGluICoqTXVsdGlsYXllciBQZXJjZXB0cm9ucyAoTUxQcykqKiByZXF1aXJlcyBjYXJlZnVsIGNvbnNpZGVyYXRpb24sIGFzIG5ldXJhbCBuZXR3b3JrcyB0eXBpY2FsbHkgZXhwZWN0IGNvbXBsZXRlIGlucHV0IGRhdGEuDQoNCkFsbCBpbXB1dGF0aW9uIG1ldGhvZHMgZGlzY3Vzc2VkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uIGNhbiBiZSB1c2VkIHRvIHByZXByb2Nlc3MgZGF0YSB3aXRoIG1pc3NpbmcgdmFsdWVzIGZvciBNTFAgbW9kZWxpbmcuIFRoZSBnZW5lcmFsIHJlY29tbWVuZGF0aW9uIGlzIHRvIHVzZSBzdG9jaGFzdGljIG11bHRpcGxlIGltcHV0YXRpb24gdGVjaG5pcXVlcywgc3VjaCBhcyBNSUNFIChNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zKSBhbmQgb3RoZXIgcmFuZG9tIHJlcGxhY2VtZW50IG1ldGhvZHMgdGhhdCBtYWtlIHVzZSBvZiB0aGUgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIG9mIHRoZSB1bmRlcmx5aW5nIGZlYXR1cmUgdmFyaWFibGVzLg0KDQoNCiMjIyBDYXRlZ29yaWNhbCBFbmNvZGluZw0KDQoqKkNhdGVnb3JpY2FsIGVuY29kaW5nKiogdHJhbnNmb3JtcyBub24tbnVtZXJpYyB2YXJpYWJsZXMgaW50byBudW1lcmljYWwgcmVwcmVzZW50YXRpb25zIGZvciBtYWNoaW5lIGxlYXJuaW5nLiANCg0KKiBGb3IgKipub21pbmFsIGRhdGEgKG5vIGluaGVyZW50IG9yZGVyKSoqLCBjb21tb24gbWV0aG9kcyBpbmNsdWRlIG9uZS1ob3QgZW5jb2RpbmcsIHdoaWNoIGNyZWF0ZXMgYmluYXJ5IGNvbHVtbnMgZm9yIGVhY2ggY2F0ZWdvcnkgKGlkZWFsIGZvciA8MTUgY2F0ZWdvcmllcyksIGFuZCB0YXJnZXQgZW5jb2RpbmcsIHdoaWNoIHJlcGxhY2VzIGNhdGVnb3JpZXMgd2l0aCB0aGUgbWVhbiBvZiB0aGUgdGFyZ2V0IHZhcmlhYmxlIChiZXR0ZXIgZm9yIGhpZ2gtY2FyZGluYWxpdHkgZmVhdHVyZXMgYnV0IHJlcXVpcmVzIGNhcmVmdWwgdmFsaWRhdGlvbiB0byBhdm9pZCBsZWFrYWdlKS4gDQoNCiogRm9yICoqb3JkaW5hbCBkYXRhIChuYXR1cmFsIG9yZGVyKSoqLCBvcmRpbmFsIGVuY29kaW5nIGFzc2lnbnMgaW50ZWdlcnMgd2hpbGUgcHJlc2VydmluZyB0aGUgbG9naWNhbCBzZXF1ZW5jZSAoZS5nLiwgU21hbGw9MSwgTWVkaXVtPTIpLiANCg0KKiAqKkhpZ2gtY2FyZGluYWxpdHkgZmVhdHVyZXMqKiAoZS5nLiwgWklQIGNvZGVzKSBtYXkgdXNlIGZyZXF1ZW5jeSBlbmNvZGluZyAocmVwbGFjaW5nIGNhdGVnb3JpZXMgd2l0aCB0aGVpciBvY2N1cnJlbmNlIGNvdW50cykgb3IgZW1iZWRkaW5nIGxheWVycyBpbiBuZXVyYWwgbmV0d29ya3MgdG8gY2FwdHVyZSBjb21wbGV4IHJlbGF0aW9uc2hpcHMuIA0KDQoNClRoZSBjaG9pY2UgZGVwZW5kcyBvbiB0aGUgbW9kZWwgdHlwZSBhbmQgZGF0YSBjaGFyYWN0ZXJpc3RpY3M6IA0KDQoqICoqVHJlZS1iYXNlZCBtb2RlbHMqKiAoZS5nLiwgUmFuZG9tIEZvcmVzdHMpIG9mdGVuIGhhbmRsZSBsYWJlbCBlbmNvZGluZyB3ZWxsLg0KDQoqICoqTGluZWFyIG1vZGVscyBhbmQgbmV1cmFsIG5ldHdvcmtzKiogdHlwaWNhbGx5IHJlcXVpcmUgb25lLWhvdCBvciB0YXJnZXQgZW5jb2RpbmcuIA0KDQpDcm9zcy12YWxpZGF0aW9uIGlzIGVzc2VudGlhbCB0byBldmFsdWF0ZSB0aGUgaW1wYWN0IG9mIGVuY29kaW5nIHN0cmF0ZWdpZXMgb24gbW9kZWwgcGVyZm9ybWFuY2UuDQoNCg0KDQojIyMgRGF0YSBTcGxpdHRpbmcNCg0KUHJvcGVyIGRhdGEgc3BsaXR0aW5nIGlzIGNydWNpYWwgZm9yIHRyYWluaW5nICoqTXVsdGlsYXllciBQZXJjZXB0cm9ucyAoTUxQcykqKiB0byBlbnN1cmUgcm9idXN0IG1vZGVsIGV2YWx1YXRpb24gYW5kIHByZXZlbnQgb3ZlcmZpdHRpbmcuIEZvciBjcm9zcy1zZWN0aW9uYWwgZGF0YSwgYSByYW5kb20gc3BsaXR0aW5nIGlzIHJlcXVpcmVkOg0KDQoqIFR3by13YXkgc3BsaXR0aW5nIGZvciB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhIHNldHMuIFRoZSB0cmFpbmluZyBhbmQgdGVzdCBkYXRhIHJhdGlvIGlzIHVzdWFsbHkgNzAlLTMwJSBvciA4MCUtMjAlLg0KDQoqICoqTmVzdGVkIHJhbmRvbSBzcGxpdHRpbmcgZm9yIGNyb3NzLXZhbGlkYXRpb24qKiBpbnZvbHZlcyB0d28gbGF5ZXJzIG9mIGRhdGEgcGFydGl0aW9uaW5nOiBhbiBvdXRlciBsb29wIGZvciBwZXJmb3JtYW5jZSBldmFsdWF0aW9uIGFuZCBhbiBpbm5lciBsb29wIGZvciBoeXBlcnBhcmFtZXRlciB0dW5pbmcuIFRoaXMgYXBwcm9hY2ggZW5zdXJlcyB1bmJpYXNlZCBtb2RlbCBhc3Nlc3NtZW50IGJ5IHByZXZlbnRpbmcgZGF0YSBsZWFrYWdlIGJldHdlZW4gdGhlIHR1bmluZyBhbmQgZXZhbHVhdGlvbiBwaGFzZXMuDQoNClRoZXJlIGFyZSBvdGhlciByYW5kb20gZGF0YSBzcGxpdHRpbmcgbWV0aG9kcyBiYXNlZCBvbiB0aGUgZGF0YSBzdHJ1Y3R1cmVzIHRoYXQgcmVxdWlyZSBzcGVjaWFsIGRlc2lnbiBzbyB0aGF0IHRoZSByYW5kb21seSBzcGxpdCBkYXRhIHJldGFpbiB0aGUgc2FtZSBwcm9iYWJpbGl0eSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgc2V0Lg0KDQoNCiMjIE1MUCBBcmNoaXRlY3R1cmUgRGVzaW5nDQoNCkluIG5ldXJhbCBuZXR3b3JrcywgTUxQIChNdWx0aWxheWVyIFBlcmNlcHRyb24pIHNpemUgYW5kIGRlcHRoIHJlZmVyIHRvIGl0cyBhcmNoaXRlY3R1cmUsIHBhcnRpY3VsYXJseSB0aGUgbnVtYmVyIG9mIG5ldXJvbnMgKHNpemUpIGFuZCB0aGUgbnVtYmVyIG9mIGhpZGRlbiBsYXllcnMgKGRlcHRoKS4gVGhlc2UgZmFjdG9ycyBzaWduaWZpY2FudGx5IGltcGFjdCB0aGUgbW9kZWwncyBjYXBhY2l0eSwgdHJhaW5pbmcgZHluYW1pY3MsIGFuZCBnZW5lcmFsaXphdGlvbiBhYmlsaXR5Lg0KDQoqICoqU2hhbGxvdyBuZXR3b3JrcyoqICgxLTIgaGlkZGVuIGxheWVycykgbWF5IHN1ZmZpY2UgZm9yIHNpbXBsZSB0YXNrcy4gICoqU2hhbGxvdyBuZXR3b3JrcyoqIA0KICArIGNhbiBhcHByb3hpbWF0ZSBhbnkgY29udGludW91cyBmdW5jdGlvbiAoVW5pdmVyc2FsIEFwcHJveGltYXRpb24gVGhlb3JlbSkuDQogICsgb2Z0ZW4gc3VmZmljaWVudCBmb3Igc2ltcGxlIHRhc2tzIChlLmcuLCBzbWFsbCB0YWJ1bGFyIGRhdGFzZXRzKS4NCiAgKyBhcmUgbGVzcyBwcm9uZSB0byBvdmVyZml0dGluZyBidXQgbWF5IHVuZGVyZml0IGNvbXBsZXggZGF0YS4NCg0KKiAqKkRlZXAgbmV0d29ya3MqKiBjYW4gbW9kZWwgY29tcGxleCBwYXR0ZXJucyBidXQgcmlzayBvdmVyZml0dGluZyBhbmQgaW5jcmVhc2VkIGNvbXB1dGF0aW9uYWwgY29zdC4gKipEZWVwIG5ldHdvcmtzKiogYXJlDQogICsgYmV0dGVyIGF0IGxlYXJuaW5nIGhpZXJhcmNoaWNhbCByZXByZXNlbnRhdGlvbnMuDQogICsgdXNlZnVsIGZvciBoaWdoLWRpbWVuc2lvbmFsIGRhdGEgKGUuZy4sIGltYWdlcywgTkxQIGFmdGVyIGZlYXR1cmUgZXh0cmFjdGlvbikuDQogICsgbW9yZSBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIGFuZCBwcm9uZSB0byBvdmVyZml0dGluZyAocmVxdWlyZXMgcmVndWxhcml6YXRpb24gbGlrZSBEcm9wb3V0LCBMMikuDQoNCg0KKiAqKk1MUCBTaXplIChOdW1iZXIgb2YgTmV1cm9ucyBwZXIgTGF5ZXIpKiogaXMgYWxzbyBpbXBhY3RmdWwgb24gdGhlIG1vZGVsIHBlcmZvcm1hbmNlLg0KICArIFdpZGVyIExheWVycyAobW9yZSBuZXVyb25zKSBpbmNyZWFzZSBtb2RlbCBjYXBhY2l0eSwgYWxsb3dpbmcgbW9yZSBjb21wbGV4IGZ1bmN0aW9uIGFwcHJveGltYXRpb24uIEl0IGNhbiBsZWFkIHRvIG92ZXJmaXR0aW5nIGlmIG5vdCByZWd1bGFyaXplZCBhbmQgdXNlIG1vcmUgY29tcHV0YXRpb24gcmVzb3VyY2VzIChtb3JlIHBhcmFtZXRlcnMpLg0KICArIE5hcnJvd2VyIExheWVycyAoZmV3ZXIgbmV1cm9ucykgYXJlIGxlc3MgZXhwcmVzc2l2ZSBidXQgZmFzdGVyIHRvIHRyYWluIGJ1dCBtYXkgbGVhZCB0byB1bmRlcmZpdHRpbmcgY29tcGxleCBwYXR0ZXJucy4NCg0KKiAqKkNob29zaW5nIERlcHRoICYgU2l6ZSoqIGlzIGJhc2VkIG9uIHRoZSBmb2xsb3dpbmcgZ2VuZXJhbCByZWNvbW1lbmRhdGlvbnMNCiAgKyBTdGFydCB3aXRoIDEtMyBoaWRkZW4gbGF5ZXJzIGFuZCBhZGp1c3QgYmFzZWQgb24gcGVyZm9ybWFuY2UuDQogICsgVXNlIHNpbWlsYXIgb3IgZGVjcmVhc2luZyBsYXllciBzaXplcy4NCiAgKyBXaWRlciBsYXllcnMgZWFybHkgY2FuIGhlbHAgZmVhdHVyZSBleHRyYWN0aW9uOyBkZWVwZXIgbmV0d29ya3MgcmVmaW5lIGFic3RyYWN0aW9ucy4NCg0KDQojIyBBY3RpdmF0aW9uIEZ1bmN0aW9ucw0KDQoqKkFjdGl2YXRpb24gZnVuY3Rpb25zKiogc2VydmUgZGlzdGluY3QgcHVycG9zZXMgaW4gaGlkZGVuIGFuZCBvdXRwdXQgbGF5ZXJzLiBJbiBoaWRkZW4gbGF5ZXJzLCB0aGV5IGludHJvZHVjZSBub25saW5lYXIgdHJhbnNmb3JtYXRpb25zIHRvIGNhcHR1cmUgY29tcGxleCBwYXR0ZXJucyBpbiB0aGUgZGF0YS4gVGhlIG91dHB1dCBsYXllcidzIGFjdGl2YXRpb24gZnVuY3Rpb24gaXMgY2hvc2VuIHNwZWNpZmljYWxseSB0byBwcm9kdWNlIHByZWRpY3Rpb25zIGNvbXBhdGlibGUgd2l0aCB0aGUgdGFyZ2V0IHZhcmlhYmxlJ3MgY2hhcmFjdGVyaXN0aWNzLiANCg0KDQojIyMgSGlkZGVuIExheWVyIEFjdGl2YXRpb24NCg0KVGhlcmUgYXJlIHNldmVyYWwgYWN0aXZhdGlvbiBmdW5jdGlvbnMgZm9yIHRoZSBoaWRkZW4gbGF5ZXIuIFRoZSBSZUxVIChSZWN0aWZpZWQgTGluZWFyIFVuaXQpIGlzIHdpZGVseSB1c2VkIGR1ZSB0byBpdHMgY29tcHV0YXRpb25hbCBlZmZpY2llbmN5IGFuZCBpcyB0aGUgZGVmYXVsdCBmb3IgbWFueSBNTFAgbGlicmFyaWVzLiBUaGUgZm9sbG93aW5nIHRhYmxlIG91dGxpbmVzIHNvbWUgb2YgdGhlIGNvbW1vbmx5IHVzZWQgYWN0aXZhdGlvbiBmdW5jdGlvbnMgZm9yIGhpZGRlbiBsYXllcnMuDQoNCg0KfCBBY3RpdmF0aW9uCXwgQmVzdCBGb3IJIHwgICAgUHJvcwkgIHwgICAgQ29ucyAJfCBSZWNvbW1lbmRhdGlvbiB8DQp8Oi0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tfDotLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS18DQp8IFJlTFUJfCBEZWZhdWx0IGNob2ljZQl8IEZhc3QsIGF2b2lkcyB2YW5pc2hpbmcgZ3JhZGllbnQgKGZvciB4ID4gMCkJfCAiRHlpbmcgUmVMVSIgKGRlYWQgbmV1cm9ucykJfCBGaXJzdCB0cnkgaW4gbW9zdCBNTFBzfCANCnwgTGVha3kgUmVMVQl8IERlZXAgbmV0d29ya3MJfCBGaXhlcyBkeWluZyBSZUxVCXwgU2xpZ2h0bHkgc2xvd2VyIHRoYW4gUmVMVQl8IFVzZSBpZiBSZUxVIGZhaWxzfCANCnwgR0VMVQl8IFRyYW5zZm9ybWVycywgZGVlcCBsZWFybmluZwl8IFNtb290aCwgYmV0dGVyIGdyYWRpZW50IGZsb3cJfCBNb3JlIGNvbXB1dGUtaGVhdnkJfCAgQmVzdCBmb3IgbW9kZXJuIGRlZXAgbmV0cyAoZS5nLiwgQkVSVCwgR1BUKXwgDQp8IFN3aXNoCXwgRXhwZXJpbWVudGFsIGFsdGVybmF0aXZlCXwgQ2FuIG91dHBlcmZvcm0gUmVMVQl8IFNsb3dlciB0aGFuIFJlTFUJfCAgVHJ5IGlmIHR1bmluZyBwZXJmb3JtYW5jZXwgDQp8IFNFTFUJfCBTZWxmLW5vcm1hbGl6aW5nIG5ldHdvcmtzCXwgTm8gQmF0Y2hOb3JtIG5lZWRlZAl8IFJlcXVpcmVzIGNhcmVmdWwgaW5pdGlhbGl6YXRpb24JfCBVc2Ugb25seSBpbiBzcGVjaWZpYyBhcmNoaXRlY3R1cmVzfCANCg0KDQo8Zm9udCBjb2xvciA9ICJyZWQiPioqXGNvbG9ye3JlZH1Bdm9pZCB1c2luZyBTaWdtb2lkL1RhbmggaW4gaGlkZGVuIGxheWVycyB0aGF0IGNhdXNlIHZhbmlzaGluZyBncmFkaWVudHMgaW4gZGVlcCBuZXR3b3Jrcy4qKjwvZm9udD4NCg0KDQoNCiMjIyBPdXRwdXQgTGF5ZXIgQWN0aXZhdGlvbg0KDQpEZXBlbmRpbmcgb24gdGhlIHR5cGUgb2YgdGhlIHRhcmdldCByZXNwb25zZSwgdGhlIGNvbW1vbmx5IHVzZWQgYWN0aXZhdGlvbiBmdW5jdGlvbnMgYXJlIHN1bW1hcml6ZWQgaW4gdGhlIGZvbGxvd2luZyB0YWJsZS4NCg0KfCAgVGFzawkgICAgIHwgICAgQWN0aXZhdGlvbgkgIHwgICAgV2h5PyAgIAl8ICAgICAgRXhhbXBsZSBVc2UgQ2FzZSAgICAgfA0KfDotLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICBSZWdyZXNzaW9uICh1bmJvdW5kZWQpCXwgIExpbmVhciAobm8gYWN0aXZhdGlvbikJfCAgT3V0cHV0cyBhbnkgcmVhbCBudW1iZXIJfCAgUHJlZGljdGluZyBob3VzZSBwcmljZXN8ICANCnwgIFJlZ3Jlc3Npb24gKDAgdG8gMSkJfCAgU2lnbW9pZAl8ICBCb3VuZHMgb3V0cHV0IHRvIFswLCAxXQl8ICBQcmVkaWN0aW5nIHByb2JhYmlsaXRpZXN8ICANCnwgIEJpbmFyeSBDbGFzc2lmaWNhdGlvbgl8ICBTaWdtb2lkCXwgIE91dHB1dHMgcHJvYmFiaWxpdHkgKDAgb3IgMSkJfCAgU3BhbSBkZXRlY3Rpb258ICANCnwgIE11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb24JfCAgU29mdG1heAl8ICBQcm9iYWJpbGl0aWVzIHN1bSB0byAxCXwgIE1OSVNUIGRpZ2l0IGNsYXNzaWZpY2F0aW9ufCAgDQp8ICBNdWx0aWxhYmVsIENsYXNzaWZpY2F0aW9uCXwgIFNpZ21vaWQgKHBlciBjbGFzcykJfCAgSW5kZXBlbmRlbnQgcHJvYmFiaWxpdGllcwl8ICBJbWFnZSB0YWdnaW5nfCAgDQoNCg0KPGZvbnQgY29sb3IgPSAicmVkIj4qKlxjb2xvcntyZWR9TmV2ZXIgdXNlIFJlTFUgaW4gdGhlIG91dHB1dCBsYXllciB1bmxlc3Mgb3V0cHV0cyBtdXN0IGJlIOKJpTAsIGUuZy4sIGNvdW50IHByZWRpY3Rpb24gIGxpa2UgUG9pc3NvbiByZWdyZXNzaW9uKS4qKjwvZm9udD4NCg0KDQojIyMgR3VpZGVsaW5lIGZvciBIaWRkZW4gYW5kIE91dHB1dCBMYXllcnMgDQoNCg0KVGhlIGZvbGxvd2luZyB0YWJsZSBwcm92aWRlcyBhIGd1aWRlbGluZSBmb3IgY2hvb3NpbmcgYXBwcm9wcmlhdGUgYWN0aXZhdGlvbiBmdW5jdGlvbnMgaW4gTUxQLg0KDQp8ICBTY2VuYXJpbyAgIHwgCUhpZGRlbiBMYXllciAgfCAJT3V0cHV0IExheWVyICB8DQp8Oi0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLXwNCnwgIEdlbmVyYWwgTUxQCXwgIFJlTFUJfCAgVGFzay1kZXBlbmRlbnQgKHNlZSBhYm92ZSl8ICANCnwgIERlZXAgTmV0d29yawl8ICBHRUxVL0xlYWt5IFJlTFUgfCAgCVRhc2stZGVwZW5kZW50fCAgDQp8ICBUcmFuc2Zvcm1lciBNb2RlbAl8ICBHRUxVCXwgIFNvZnRtYXgvU2lnbW9pZHwgIA0KfCAgU2VsZi1Ob3JtYWxpemluZyBOZXQJfCAgU0VMVQl8ICBUYXNrLWRlcGVuZGVudHwgIA0KDQoNCiMjIFRyYWluaW5nLCBPcHRpbWl6YXRpb24gDQoNCkVmZmVjdGl2ZSB0cmFpbmluZyByZXF1aXJlcyBwcm9wZXIgb3B0aW1pemF0aW9uIHRlY2huaXF1ZXMgYW5kIGh5cGVycGFyYW1ldGVyIHR1bmluZy4NCg0KKiAqKkxvc3MgRnVuY3Rpb24gU2VsZWN0aW9uKiogaXMgZGVwZW5kZW50IG9uIHRoZSBhcHBsaWNhdGlvbnM6DQogICsgKipDbGFzc2lmaWNhdGlvbioqOiBDcm9zcy1lbnRyb3B5IGxvc3MNCiAgKyAqKlJlZ3Jlc3Npb24qKjogTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpIG9yIE1lYW4gQWJzb2x1dGUgRXJyb3IgKE1BRSkNCg0KKiAqKk9wdGltaXplciBDaG9pY2UqKiBpcyBhbHNvIGRlcGVuZGVudCBvbiB0aGUgYXBwbGljYXRpb25zIGFuZCB0aGUgcmVxdWlyZWQgY29tcHV0YXRpb25hbCByZXNvdXJjZXMgZm9yIHRoZSBhcHBsaWNhdGlvbnMuDQogICsgU3RvY2hhc3RpYyBHcmFkaWVudCBEZXNjZW50IChTR0QpOiBCYXNpYyBidXQgcmVxdWlyZXMgY2FyZWZ1bCBsZWFybmluZyByYXRlIHR1bmluZy4gDQogICsgQWRhcHRpdmUgT3B0aW1pemVycyAoQWRhbSwgUk1TcHJvcCk6IEF1dG9tYXRpY2FsbHkgYWRqdXN0IGxlYXJuaW5nIHJhdGVzLCBvZnRlbiBsZWFkaW5nIHRvIGZhc3RlciBjb252ZXJnZW5jZS4gQSB0b28taGlnaCBsZWFybmluZyByYXRlIGNhdXNlcyBpbnN0YWJpbGl0eSwgd2hpbGUgYSB0b28tbG93IHJhdGUgc2xvd3MgY29udmVyZ2VuY2UuDQoNCiogKipIeXBlcnBhcmFtZXRlciBUdW5pbmcqKg0KTWV0aG9kcyBsaWtlIGdyaWQgc2VhcmNoLCByYW5kb20gc2VhcmNoLCBvciBCYXllc2lhbiBvcHRpbWl6YXRpb24gaGVscCBmaW5kIG9wdGltYWwgY29uZmlndXJhdGlvbnMgZWZmaWNpZW50bHkuDQoNClwNCg0KIyMgUmVsYXRpdmUgSW1wcm92ZW1lbnQgaW4gUGVyZm9ybWFuY2UNCg0KV2hlbiBldmFsdWF0aW5nIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLCBpdCBpcyBlc3NlbnRpYWwgdG8gcXVhbnRpZnkgaG93IG11Y2ggYSBwcm9wb3NlZCBuZXVyYWwgbmV0d29yayAoTk4pIG1vZGVsIGltcHJvdmVzIHVwb24gYSBzaW1wbGVyIGJhc2UgbW9kZWwgKGUuZy4sIGxpbmVhciByZWdyZXNzaW9uIGZvciByZWdyZXNzaW9uIHRhc2tzLCBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiBmb3IgY2xhc3NpZmljYXRpb24pLiBNZWFzdXJpbmcgcmVsYXRpdmUgaW1wcm92ZW1lbnQgaGVscHMgZGV0ZXJtaW5lIHdoZXRoZXIgdGhlIGFkZGVkIGNvbXBsZXhpdHkgb2YgYSBuZXVyYWwgbmV0d29yayBqdXN0aWZpZXMgaXRzIGFkb3B0aW9uLiBXZSBuZXh0IGJyaWVmbHkgZGlzY3VzcyBrZXkgbWV0aG9kcyBmb3IgY29tcHV0aW5nIGFuZCBpbnRlcnByZXRpbmcgcmVsYXRpdmUgaW1wcm92ZW1lbnQsIGFsb25nIHdpdGggcHJhY3RpY2FsIGNvbnNpZGVyYXRpb25zLiBSZWNhbGwgdGhhdCBwZXJmb3JtYW5jZSBtZXRyaWNzIGZvciByZWdyZXNzaW9uIGFuZCBjbGFzc2lmaWNhdGlvbiBhcmUgbGlzdGVkLg0KDQogICsgKipDbGFzc2lmaWNhdGlvbioqOiBBY2N1cmFjeSwgUHJlY2lzaW9uLCBSZWNhbGwsIEYxLXNjb3JlLCBST0MtQVVDLg0KICArICoqUmVncmVzc2lvbioqOiBNU0UsIFJNU0UsICRSXjIkLg0KICANCioqUmVsYXRpdmUgaW1wcm92ZW1lbnQqKiBtZWFzdXJlcyBob3cgbXVjaCBiZXR0ZXIgKG9yIHdvcnNlKSBhIG5ldXJhbCBuZXR3b3JrIHBlcmZvcm1zIGNvbXBhcmVkIHRvIGEgYmFzZWxpbmUgbW9kZWwuIEl0IGlzIHR5cGljYWxseSBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlIHJlZHVjdGlvbiBpbiBlcnJvciAoZm9yIHJlZ3Jlc3Npb24pIG9yIGEgcGVyY2VudGFnZSBpbmNyZWFzZSBpbiBhY2N1cmFjeS9wcmVjaXNpb24vcmVjYWxsIChmb3IgY2xhc3NpZmljYXRpb24pLiAgVGhlIGdlbmVyYWwgZm9ybXVsYSBpcyBnaXZlbiBieQ0KDQokJA0KXHRleHR7UmVsYXRpdmUgSW1wcm92ZW1lbnR9ID0gXGZyYWN7XHRleHR7TWV0cmljfV97XHRleHR7QmFzZX19LVx0ZXh0e01ldHJpY31fe1x0ZXh0e05OfX19e1x0ZXh0e01ldHJpY31fe1x0ZXh0e0Jhc2V9fX0NCiQkDQogIA0KQXMgYW4gZXhhbXBsZSwgY29tcGFyZSB0aGUgUk1TRSBiZXR3ZWVuIGxpbmVhciByZWdyZXNzaW9uIChmaXQgaXQgdG8gdGhlIHNjYWxlZCBkYXRhKSBhbmQgTk4gbW9kZWwuIEFzc3VtZSB0aGUgYmFzZSBtb2RlbCBSTVNFID0gMTAuMCBhbmQgTk4gbW9kZWwgUk1TRSA9IDcuIFRoZW4NCg0KJCQNClx0ZXh0e0ltcHJvdmVtZW50fSA9IFxmcmFjezEwLTd9ezEwfSA9IDMwXCUuDQokJA0KDQoqKkludGVycHJldGF0aW9uKio6IFRoZSBOTiByZWR1Y2VzIHByZWRpY3Rpb24gZXJyb3IgYnkgMzAlIGNvbXBhcmVkIHRvIGxpbmVhciByZWdyZXNzaW9uLiAgDQogIA0KICANClwNCiAgDQojIE1MUCBSZWdyZXNzaW9uDQoNClRoaXMgc2VjdGlvbiBmb2N1c2VzIG9uIGltcGxlbWVudGluZyBNTFAgcmVncmVzc2lvbiB1c2luZyB0aGUgQm9zdG9uIEhvdXNpbmcgZGF0YXNldCwgYWxvbmcgd2l0aCB0aGUgUiAqKm5ldXJhbG5ldCoqIGxpYnJhcnkgYW5kIG90aGVyIHN1cHBvcnRpbmcgbGlicmFyaWVzIGZvciBkYXRhIHByZXBhcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uLiBXZSB3aWxsIGZvbGxvdyB0aGUgYmFzaWMgc3RlcHMgb3V0bGluZWQgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24uIFdlIHdpbGwgZml0IG9uZS1oaWRkZW4tbGF5ZXIgYW5kIHR3by1oaWRkZW4tbGF5ZXIgTUxQIHRvIHByZWRpY3QgdGhlIG1lZGlhbiBob3VzZSB2YWx1ZS4NCg0KV2UgdXNlICoqbWluLW1heCBzY2FsaW5nIG1ldGhvZCoqIGZvciBhbGwgbnVtZXJpY2FsIHZhcmlhYmxlcy4gQWxsIGZlYXR1cmVzIGFyZSBudW1lcmljYWwsIG5vIGNhdGVnb3JpY2FsIGVuY29kaW5nIGlzIG5lZWRlZC4gV2UgdXNlIHJhbmRvbSBzcGxpdHRpbmcgKDcwLTMwKSB0byBjcmVhdGUgdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YSBzZXRzLg0KDQpgYGB7cn0NCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzDQojIGxpYnJhcnkobmV1cmFsbmV0KQ0KIyBsaWJyYXJ5KE1BU1MpICAgICAgICMgRm9yIEJvc3RvbiBIb3VzaW5nIGRhdGFzZXQNCiMgbGlicmFyeShnZ3Bsb3QyKSAgICAjIEZvciB2aXN1YWxpemF0aW9uDQojIGxpYnJhcnkoY2FyZXQpICAgICAgIyBPbmx5IGZvciBkYXRhIHNwbGl0dGluZyAod2Ugd29uJ3QgdXNlIGl0cyBtb2RlbGluZyBmdW5jdGlvbnMpDQojIExvYWQgQm9zdG9uIEhvdXNpbmcgZGF0YXNldA0KZGF0YShCb3N0b24pDQoNCiMgQ2hlY2sgc3RydWN0dXJlIGFuZCBzdW1tYXJ5DQojc3RyKEJvc3RvbikNCiNzdW1tYXJ5KEJvc3RvbikNCg0KIyBGZWF0dXJlIHNjYWxpbmcgLSBub3JtYWxpemUgYWxsIHZhcmlhYmxlcyB0byBbMCwxXSByYW5nZQ0Kbm9ybWFsaXplIDwtIGZ1bmN0aW9uKHgpIHsNCiAgcmV0dXJuICgoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSkNCn0NCg0KYm9zdG9uLnNjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKGxhcHBseShCb3N0b24sIG5vcm1hbGl6ZSkpDQoNCiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzKQ0KTiA8LSBsZW5ndGgoYm9zdG9uLnNjYWxlZCRtZWR2KQ0KIyBDcmVhdGUgdHJhaW4tdGVzdCBzcGxpdCAoNzAtMzApDQp0cmFpbi5yZWcuaW5kZXggPC0gc2FtcGxlKDE6TiwgIGZsb29yKDAuNypOKSwgcmVwbGFjZSA9IEZBTFNFKQ0KdHJhaW4ucmVnLmRhdGEgPC0gYm9zdG9uLnNjYWxlZFt0cmFpbi5yZWcuaW5kZXgsIF0NCnRlc3QucmVnLmRhdGEgPC0gYm9zdG9uLnNjYWxlZFstdHJhaW4ucmVnLmluZGV4LCBdDQoNCiMgQ2hlY2sgZGltZW5zaW9ucw0KI2RpbSh0cmFpbl9kYXRhKQ0KI2RpbSh0ZXN0X2RhdGEpDQpgYGANCg0KIyMgT25lLWhpZGRlbi1sYXllciBQZXJjZXB0cm9uDQoNCldlIGZpcnN0IGJ1aWxkIGEgc2luZ2xlIGhpZGRlbiBsYXllciBwZXJjZXB0cm9uIG1vZGVsLiBXZSB3aWxsIHR1bmUgdGhyZWUgaHlwZXJwYXJhbWV0ZXJzOiB0aGUgbnVtYmVyIG9mIG5vZGVzIGluIHRoZSBoaWRkZW4gbGF5ZXIsIGxlYXJuaW5nIHJhdGUsIGFuZCBhY3RpdmF0aW9uIGZ1bmN0aW9uIHVzaW5nIGdyaWQgc2VhcmNoLiBUaGUgcGVyZm9ybWFuY2UgbWV0cmljIHVzZWQgdG8gc2VsZWN0IHRoZSBvcHRpbWFsIGNvbWJpbmF0aW9uIG9mIHZhbHVlcyBvZiBoeXBlcnBhcmFtZXRlcnMgaXMgUk1TRS4NCg0KYGBge3J9DQojIERlZmluZSBncmlkIG9mIGh5cGVycGFyYW1ldGVycw0KaHlwZXIuZ3JpZC5yZWcgPC0gZXhwYW5kLmdyaWQoDQogIGxheWVyMSA9IGMoNSwgMTAsIDE1KSwNCiAgbGVhcm5pbmcucmF0ZSA9IGMoMC4wMSwgMC4xKSwNCiAgYWN0aXZhdGlvbiA9IGMoImxvZ2lzdGljIiwgInRhbmgiKQ0KKQ0KDQojIEluaXRpYWxpemUgcmVzdWx0cyBzdG9yYWdlDQpybXNlID0gTlVMTA0KI2xheWVyMSA9IE5VTEwNCiNsZWFybmluZ3JhdGUgPSBOVUxMDQojYWN0aXZhdGlvbiA9IE5VTEwNCg0KYmVzdC5yZWcucm1zZSA8LSBJbmYNCmJlc3QucmVnLm1vZGVsIDwtIE5VTEwNCg0KIyBQZXJmb3JtIGdyaWQgc2VhcmNoDQpmb3IoaSBpbiAxOm5yb3coaHlwZXIuZ3JpZC5yZWcpKSB7DQogICMgR2V0IGN1cnJlbnQgY29uZmlndXJhdGlvbg0KICBsYXllciA8LSBoeXBlci5ncmlkLnJlZyRsYXllcjFbaV0NCiAgbHIgPC0gaHlwZXIuZ3JpZC5yZWckbGVhcm5pbmcucmF0ZVtpXQ0KICBhY3QgPC0gaHlwZXIuZ3JpZC5yZWckYWN0aXZhdGlvbltpXQ0KICANCiAgIyBUcmFpbiBtb2RlbA0KICBzZXQuc2VlZCgxMjMpDQogIG1vZGVsLnJlZyA8LSBuZXVyYWxuZXQoDQogICAgICBtZWR2IH4gLiwNCiAgICAgIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSwNCiAgICAgIGhpZGRlbiA9IGxheWVyLA0KICAgICAgYWN0LmZjdCA9IGFjdCwNCiAgICAgIGxpbmVhci5vdXRwdXQgPSBUUlVFLCAgIyBGb3IgcmVncmVzc2lvbg0KICAgICAgbGVhcm5pbmdyYXRlID0gbHIsDQogICAgICBhbGdvcml0aG0gPSAicnByb3ArIiwNCiAgICAgIHN0ZXBtYXggPSAxZTUgKQ0KICANCg0KICAgICMgTWFrZSBwcmVkaWN0aW9ucw0KICAgIHByZWRzLnJlZyA8LSBwcmVkaWN0KG1vZGVsLnJlZywgdGVzdC5yZWcuZGF0YVssIC1uY29sKHRlc3QucmVnLmRhdGEpXSkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBSTVNFDQogICAgcm1zZS5yZWcgPC0gc3FydChtZWFuKChwcmVkcy5yZWcgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpXjIpKQ0KICAgIA0KICAgICMgU3RvcmUgcmVzdWx0cw0KICAgIHJtc2VbaV0gPSBybXNlLnJlZw0KICAgIA0KICAgICMgVXBkYXRlIGJlc3QgbW9kZWwNCiAgICBpZihybXNlLnJlZyA8IGJlc3QucmVnLnJtc2UpIHsNCiAgICAgIGJlc3QucmVnLnJtc2UgPC0gcm1zZS5yZWcNCiAgICAgIGJlc3QucmVnLm1vZGVsIDwtIG1vZGVsLnJlZyANCiAgICAgIGJlc3QucmVnLnBhcmFtcyA8LSBoeXBlci5ncmlkLnJlZ1tpLCBdDQogICAgfQ0KfQ0KDQpyZXN1bHRzLnJlZ05OIDwtIGh5cGVyLmdyaWQucmVnDQpyZXN1bHRzLnJlZ05OJHJtc2UgPC0gcm1zZQ0KDQoNCiMgVmlldyByZXN1bHRzIHNvcnRlZCBieSBSTVNFDQpwYW5kZXIocmVzdWx0cy5yZWdOTltvcmRlcihyZXN1bHRzLnJlZ05OJHJtc2UpLCBdWzEsXSkNCmBgYA0KDQpXaXRoIHRoZSBhYm92ZSBjb21iaW5hdGlvbiBvZiBvcHRpbWFsIGh5cGVycGFyYW1ldGVyIHZhbHVlcywgd2UgdHJhaW4gdGhlIGZpbmFsIHNpbmdsZSBoaWRkZW4gbGF5ZXIgcGVyY2VwdHJvbiBtb2RlbC4NCg0KYGBge3J9DQojIFRyYWluIHRoZSBmaW5hbCBtb2RlbCB3aXRoIGJlc3QgcGFyYW1ldGVycw0KZmluYWwucmVnLm1vZGVsIDwtIG5ldXJhbG5ldCgNCiAgbWVkdiB+IC4sDQogIGRhdGEgPSB0cmFpbi5yZWcuZGF0YSwNCiAgaGlkZGVuID0gYmVzdC5yZWcucGFyYW1zJGxheWVyMSwNCiAgYWN0LmZjdCA9IGJlc3QucmVnLnBhcmFtcyRhY3RpdmF0aW9uLA0KICBsaW5lYXIub3V0cHV0ID0gVFJVRSwNCiAgbGVhcm5pbmdyYXRlID0gYmVzdC5yZWcucGFyYW1zJGxlYXJuaW5nX3JhdGUsDQogIGFsZ29yaXRobSA9ICJycHJvcCsiLA0KICBzdGVwbWF4ID0gMWU1DQopDQoNCiMgUGxvdCB0aGUgbmV1cmFsIG5ldHdvcmsNCnBsb3QoZmluYWwucmVnLm1vZGVsKQ0KYGBgDQoNClRoZSBhYm92ZSBOTiBwbG90IHNob3dzIHRoZSBhcmNoaXRlY3R1cmUgb2YgdGhlIGZpbmFsIG9uZS1oaWRkZW4tbGF5ZXIgcGVyY2VwdHJvbiBtb2RlbC4gTmV4dCwgd2Ugd2lsbCB1c2UgaXQgdG8gbWFrZSBwcmVkaWN0aW9ucy4NCg0KYGBge3J9DQojIE1ha2UgcHJlZGljdGlvbnMgb24gdGVzdCBzZXQNCnByZWQuTk4xIDwtIHByZWRpY3QoZmluYWwucmVnLm1vZGVsLCB0ZXN0LnJlZy5kYXRhWywgLW5jb2wodGVzdC5yZWcuZGF0YSldKQ0KDQojIENhbGN1bGF0ZSBwZXJmb3JtYW5jZSBtZXRyaWNzDQpybXNlLk5OMSA8LSBzcXJ0KG1lYW4oKHByZWQuTk4xICAtIHRlc3QucmVnLmRhdGEkbWVkdileMikpDQptYWUuTk4xIDwtIG1lYW4oYWJzKHByZWQuTk4xICAtIHRlc3QucmVnLmRhdGEkbWVkdikpDQpyX3NxdWFyZWQuTk4xIDwtIGNvcihwcmVkLk5OMSAsIHRlc3QucmVnLmRhdGEkbWVkdileMg0KDQojIGNhdCgiUGVyZm9ybWFuY2UgTWV0cmljczpcbiIpDQojIGNhdCgiUk1TRToiLCBybXNlLCAiXG4iKQ0KIyBjYXQoIk1BRToiLCBtYWUsICJcbiIpDQojIGNhdCgiUi1zcXVhcmVkOiIsIHJfc3F1YXJlZCwgIlxuIikNCg0KIyBQbG90IHByZWRpY3Rpb25zIHZzIGFjdHVhbA0KcGxvdC5OTjEuZGF0YSA8LSBkYXRhLmZyYW1lKA0KICBBY3R1YWwgPSB0ZXN0LnJlZy5kYXRhJG1lZHYsDQogIFByZWRpY3RlZCA9IHByZWQuTk4xIA0KKQ0KDQpnZ3Bsb3QocGxvdC5OTjEuZGF0YSwgYWVzKHggPSBBY3R1YWwsIHkgPSBQcmVkaWN0ZWQpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAiZGFya3JlZCIpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4PTAuODUsIHk9LjIsIA0KICAgICAgICAgICBsYWJlbD1wYXN0ZSgiUi5zcSA9Iiwgcm91bmQocl9zcXVhcmVkLk5OMSw0KSksIGNvbG9yPSJibHVlIikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHg9MC44NSwgeT0uMTMsIA0KICAgICAgICAgICBsYWJlbD1wYXN0ZSgiUk1TRSA9Iiwgcm91bmQocm1zZS5OTjEsNCkpLCBjb2xvcj0iYmx1ZSIpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4PTAuODUsIHk9LjA2LCANCiAgICAgICAgICAgbGFiZWw9cGFzdGUoIiAgTUFFID0iLCByb3VuZChtYWUuTk4xLDQpKSwgY29sb3I9ImJsdWUiKSArDQogIGdndGl0bGUoIkFjdHVhbCB2cyBQcmVkaWN0ZWQgVmFsdWVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpUaGUgZmlndXJlIGFib3ZlIGRlbW9uc3RyYXRlcyBhIHN0cm9uZyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgdGFyZ2V0IHZhbHVlcyBhbmQgdGhlIHNjYWxlZCB0YXJnZXQgdmFsdWVzLiBCb3RoIHRoZSBtZWFuIHNxdWFyZWQgZXJyb3IgKE1TRSkgYW5kIG1lYW4gYWJzb2x1dGUgZXJyb3IgKE1BRSkgbWV0cmljcyBhcmUgZGlzcGxheWVkIG9uIHRoZSBwbG90LiBOZXh0LCB3ZSBmaXQgYSBjbGFzc2ljYWwgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdG8gdGhlIHNjYWxlZCBkYXRhIGFuZCBwZXJmb3JtIGEgY29tcGFyYXRpdmUgYW5hbHlzaXMgYmV0d2VlbiB0aGlzIGJhc2VsaW5lIGxpbmVhciByZWdyZXNzaW9uIGFuZCBvdXIgbmV1cmFsIG5ldHdvcmsgbW9kZWwuDQoNCg0KDQpgYGB7cn0NCiMgbGlicmFyeShnZ3Bsb3QyKQ0KIyBUcmFpbiBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbA0KbG1fbW9kZWwgPC0gbG0obWVkdiB+IC4sIGRhdGEgPSB0ZXN0LnJlZy5kYXRhKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMNCmxtX3ByZWRpY3Rpb25zIDwtIHByZWRpY3QobG1fbW9kZWwsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQoNCiMgQ2FsY3VsYXRlIHBlcmZvcm1hbmNlIG1ldHJpY3MNCmxtX3Jtc2UgPC0gc3FydChtZWFuKChsbV9wcmVkaWN0aW9ucyAtIHRlc3QucmVnLmRhdGEkbWVkdileMikpDQpsbV9tYWUgPC0gbWVhbihhYnMobG1fcHJlZGljdGlvbnMgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpKQ0KbG1fcl9zcXVhcmVkIDwtIGNvcihsbV9wcmVkaWN0aW9ucywgdGVzdC5yZWcuZGF0YSRtZWR2KV4yDQoNCg0KIyMgaW1wcm92ZW1lbnRzDQpSTVNFLmltcCA8LSByb3VuZCgobG1fcm1zZSAtIHJtc2UuTk4xKS9sbV9ybXNlICogMTAwLDIpDQpNQUUuaW1wIDwtIHJvdW5kKChsbV9tYWUgLSBtYWUuTk4xKS9sbV9tYWUgKiAxMDAsIDIpDQpSc3EuaW1wIDwtIHJvdW5kKChyX3NxdWFyZWQuTk4xIC0gbG1fcl9zcXVhcmVkKS9sbV9yX3NxdWFyZWQgKiAxMDAsMikNCg0KIyMNClBlcmZvcm1hbmNlLnRhYmxlIDwtIGRhdGEuZnJhbWUoDQogIExNID0gYyhsbV9ybXNlLCBsbV9tYWUsIGxtX3Jfc3F1YXJlZCksDQogIE5OLjEgPSBjKHJtc2UuTk4xLCBtYWUuTk4xLCByX3NxdWFyZWQuTk4xKSwNCiAgSW1wcm92ZW1lbnQucGVyY2VudGFnZSA9IGMoUk1TRS5pbXAsIE1BRS5pbXAsIFJzcS5pbXApDQopDQpyb3duYW1lcyhQZXJmb3JtYW5jZS50YWJsZSkgPC0gYygiUk1TRSIsICJNQUUiLCAiUi5zcXVhcmUiKQ0KcGFuZGVyKFBlcmZvcm1hbmNlLnRhYmxlKQ0KYGBgDQoNCg0KYGBge3J9DQojIFBsb3QgYm90aCBwcmVkaWN0aW9ucw0KY29tcGFyaXNvbi5kYXRhIDwtIGRhdGEuZnJhbWUoDQogIEFjdHVhbCA9IHRlc3QucmVnLmRhdGEkbWVkdiwNCiAgTUxQID0gcHJlZC5OTjEsDQogIExpbmVhciA9IGxtX3ByZWRpY3Rpb25zDQopDQoNCmdncGxvdChjb21wYXJpc29uLmRhdGEpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IEFjdHVhbCwgeSA9IE1MUCwgY29sb3IgPSAiTUxQIikpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IEFjdHVhbCwgeSA9IExpbmVhciwgY29sb3IgPSAiTGluZWFyIFJlZ3Jlc3Npb24iKSkgKw0KICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG9yID0gImJsYWNrIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTUxQIiA9ICJibHVlIiwgIkxpbmVhciBSZWdyZXNzaW9uIiA9ICJyZWQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk1vZGVsIENvbXBhcmlzb246IEFjdHVhbCB2cyBQcmVkaWN0ZWQiLA0KICAgICAgIHggPSAiQWN0dWFsIFZhbHVlcyIsDQogICAgICAgeSA9ICJQcmVkaWN0ZWQgVmFsdWVzIiwNCiAgICAgICBjb2xvciA9ICJNb2RlbCBUeXBlIikgKw0KICB0aGVtZSgNCiAgICBwbG90Lm1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbig0MCwgMjAsIDIwLCAyMCwgdW5pdCA9ICJwdCIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZWhlaWdodCA9IDEuMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMTApDQogICAgKQ0KYGBgDQoNClRoZSBhYm92ZSBzY2F0dGVyIHBsb3Qgb2YgdGhlIHRydWUgdGFyZ2V0IHZhbHVlcyBhbmQgcHJlZGljdGVkIHZhbHVlcyBiYXNlZCBvbiB0aGUgdHdvIG1vZGVscyBhbHNvIHNob3dzIHRoYXQgdGhlIG9uZS1oaWRkZW4tcGVyY2VwdHJvbiBtb2RlbCBvdXRwZXJmb3JtcyB0aGUgY2xhc3NpYyBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4NCg0KDQojIyBUd28taGlkZGVuLWxheWVyIFBlcmNlcHRyb24NCg0KVGhpcyBzdWJzZWN0aW9uIGV4cGxvcmVzIHdoZXRoZXIgYSB0d28taGlkZGVuLWxheWVyIHBlcmNlcHRpb24gdGhhdCBoYXMgbW9yZSBjb21wbGV4IGFyY2hpdGVjdHVyZSBjb3VsZCBpbXByb3ZlIHRoZSBwZXJmb3JtYW5jZS4NCg0KVGhlIG1vZGVsLWJ1aWxkaW5nIHByb2Nlc3MgaXMgaWRlbnRpY2FsIHRvIHRoZSBwcmV2aW91cyBvbmUtaGlkZGVuLWxheWVyIHBlcmNlcHRyb24gbW9kZWwuIFdlIHdpbGwgbm90IGRldGFpbCB0aGUgc3RlcHMgYXMgd2UgZGlkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLg0KDQpgYGB7cn0NCiMgRGVmaW5lIGdyaWQgb2YgaHlwZXJwYXJhbWV0ZXJzDQpoeXBlci5ncmlkLk5OMiA8LSBleHBhbmQuZ3JpZCgNCiAgbGF5ZXIxID0gYyg1LCAxMCwgMTUpLA0KICBsYXllcjIgPSBjKDAsIDMsIDUpLCAgIyAwIG1lYW5zIG5vIHNlY29uZCBsYXllcg0KICBsZWFybmluZy5yYXRlID0gYygwLjAxLCAwLjEpLA0KICBhY3RpdmF0aW9uID0gYygibG9naXN0aWMiLCAidGFuaCIpDQopDQoNCiMgSW5pdGlhbGl6ZSByZXN1bHRzIGluIHN0b3JhZ2UNCnJlc3VsdHMgPC0gZGF0YS5mcmFtZSgpDQpiZXN0LnJtc2UgPC0gSW5mDQpiZXN0Lm1vZGVsIDwtIE5VTEwNCg0KIyBQZXJmb3JtIGdyaWQgc2VhcmNoDQpmb3IoaSBpbiAxOm5yb3coaHlwZXIuZ3JpZC5OTjIpKSB7DQogICMgR2V0IGN1cnJlbnQgY29uZmlndXJhdGlvbg0KICBsYXllcjEgPC0gaHlwZXIuZ3JpZC5OTjIkbGF5ZXIxW2ldDQogIGxheWVyMiA8LSBoeXBlci5ncmlkLk5OMiRsYXllcjJbaV0NCiAgbHIgPC0gaHlwZXIuZ3JpZC5OTjIkbGVhcm5pbmcucmF0ZVtpXQ0KICBhY3QgPC0gaHlwZXIuZ3JpZC5OTjIkYWN0aXZhdGlvbltpXQ0KICANCiAgIyBDcmVhdGUgaGlkZGVuIGxheWVycyB2ZWN0b3INCiAgaWYobGF5ZXIyID09IDApIHsNCiAgICBoaWRkZW4ubGF5ZXJzIDwtIGMobGF5ZXIxKQ0KICB9IGVsc2Ugew0KICAgIGhpZGRlbi5sYXllcnMgPC0gYyhsYXllcjEsIGxheWVyMikNCiAgfQ0KICANCiAgIyBUcmFpbiBtb2RlbA0KICBzZXQuc2VlZCgxMjMpDQogIG1vZGVsLk5OMiA8LSB0cnlDYXRjaCh7DQogICAgbmV1cmFsbmV0KA0KICAgICAgbWVkdiB+IC4sDQogICAgICBkYXRhID0gdHJhaW4ucmVnLmRhdGEsDQogICAgICBoaWRkZW4gPSBoaWRkZW4ubGF5ZXJzLA0KICAgICAgYWN0LmZjdCA9IGFjdCwNCiAgICAgIGxpbmVhci5vdXRwdXQgPSBUUlVFLCAgIyBGb3IgcmVncmVzc2lvbg0KICAgICAgbGVhcm5pbmdyYXRlID0gbHIsDQogICAgICBhbGdvcml0aG0gPSAicnByb3ArIiwNCiAgICAgIHN0ZXBtYXggPSAxZTUNCiAgICApDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkNCiAgDQogIGlmKCFpcy5udWxsKG1vZGVsLk5OMikpIHsNCiAgICAjIE1ha2UgcHJlZGljdGlvbnMNCiAgICBwcmVkcyA8LSBwcmVkaWN0KG1vZGVsLk5OMiwgdGVzdC5yZWcuZGF0YVssIC1uY29sKHRlc3QucmVnLmRhdGEpXSkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBSTVNFDQogICAgcm1zZSA8LSBzcXJ0KG1lYW4oKHByZWRzIC0gdGVzdC5yZWcuZGF0YSRtZWR2KV4yKSkNCiAgICANCiAgICAjIFN0b3JlIHJlc3VsdHMNCiAgICByZXN1bHRzIDwtIHJiaW5kKHJlc3VsdHMsIGRhdGEuZnJhbWUoDQogICAgICBsYXllcjEgPSBsYXllcjEsDQogICAgICBsYXllcjIgPSBsYXllcjIsDQogICAgICBsZWFybmluZ19yYXRlID0gbHIsDQogICAgICBhY3RpdmF0aW9uID0gYWN0LA0KICAgICAgcm1zZSA9IHJtc2UNCiAgICApKQ0KICAgIA0KICAgICMgVXBkYXRlIGJlc3QgbW9kZWwNCiAgICBpZihybXNlIDwgYmVzdC5ybXNlKSB7DQogICAgICBiZXN0LnJtc2UgPC0gcm1zZQ0KICAgICAgYmVzdC5tb2RlbCA8LSBtb2RlbC5OTjINCiAgICAgIGJlc3QucGFyYW1zIDwtIGh5cGVyLmdyaWQuTk4yW2ksIF0NCiAgICB9DQogIH0NCn0NCg0KIyBWaWV3IHJlc3VsdHMgc29ydGVkIGJ5IFJNU0UNCnJlc3VsdHNbb3JkZXIocmVzdWx0cyRybXNlKSwgXQ0KYGBgDQoNCmBgYHtyfQ0KIyBUcmFpbiB0aGUgZmluYWwgbW9kZWwgd2l0aCBiZXN0IHBhcmFtZXRlcnMNCmZpbmFsLm1vZGVsLk5OMiA8LSBuZXVyYWxuZXQoDQogIG1lZHYgfiAuLA0KICBkYXRhID0gdHJhaW4ucmVnLmRhdGEsDQogIGhpZGRlbiA9IGlmKGJlc3QucGFyYW1zJGxheWVyMiA9PSAwKSB7DQogICAgICAgIGMoYmVzdC5wYXJhbXMkbGF5ZXIxKX0gZWxzZSB7DQogICAgICAgIGMoYmVzdC5wYXJhbXMkbGF5ZXIxLCBiZXN0LnBhcmFtcyRsYXllcjIpfSwNCiAgYWN0LmZjdCA9IGJlc3QucGFyYW1zJGFjdGl2YXRpb24sDQogIGxpbmVhci5vdXRwdXQgPSBUUlVFLA0KICBsZWFybmluZ3JhdGUgPSBiZXN0LnBhcmFtcyRsZWFybmluZy5yYXRlLA0KICBhbGdvcml0aG0gPSAicnByb3ArIiwNCiAgc3RlcG1heCA9IDFlNQ0KKQ0KDQojIFBsb3QgdGhlIG5ldXJhbCBuZXR3b3JrDQpwbG90KGZpbmFsLm1vZGVsLk5OMikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBNYWtlIHByZWRpY3Rpb25zIG9uIHRlc3Qgc2V0DQpwcmVkLk5OMiA8LSBwcmVkaWN0KGZpbmFsLm1vZGVsLk5OMiwgdGVzdC5yZWcuZGF0YVssIC1uY29sKHRlc3QucmVnLmRhdGEpXSkNCg0KIyBDYWxjdWxhdGUgcGVyZm9ybWFuY2UgbWV0cmljcw0Kcm1zZS5OTjIgPC0gc3FydChtZWFuKChwcmVkLk5OMiAgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpXjIpKQ0KbWFlLk5OMiA8LSBtZWFuKGFicyhwcmVkLk5OMiAgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpKQ0Kcl9zcXVhcmVkLk5OMiA8LSBjb3IocHJlZC5OTjIgLCB0ZXN0LnJlZy5kYXRhJG1lZHYpXjINCg0KIyMgdmVjdG9yIG9mIGVycm9yIG1ldHJpYw0KTk4yIDwtIGMocm1zZS5OTjIsIG1hZS5OTjIsIHJfc3F1YXJlZC5OTjIpDQoNCiMgUGxvdCBwcmVkaWN0aW9ucyB2cyBhY3R1YWwNCnBsb3QuZGF0YS5OTjIgPC0gZGF0YS5mcmFtZSgNCiAgQWN0dWFsID0gdGVzdC5yZWcuZGF0YSRtZWR2LA0KICBQcmVkaWN0ZWQgPSBwcmVkLk5OMiANCikNCg0KZ2dwbG90KHBsb3QuZGF0YS5OTjIsIGFlcyh4ID0gQWN0dWFsLCB5ID0gUHJlZGljdGVkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG9yID0gInJlZCIpICsNCiAgZ2d0aXRsZSgiQWN0dWFsIHZzIFByZWRpY3RlZCBWYWx1ZXMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KDQoNCg0KYGBge3J9DQojIFRyYWluIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsDQpsbV9tb2RlbCA8LSBsbShtZWR2IH4gLiwgZGF0YSA9IHRyYWluLnJlZy5kYXRhKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMNCmxtX3ByZWRpY3Rpb25zIDwtIHByZWRpY3QobG1fbW9kZWwsIHRlc3QucmVnLmRhdGFbLCAtbmNvbCh0ZXN0LnJlZy5kYXRhKV0pDQoNCiMgQ2FsY3VsYXRlIHBlcmZvcm1hbmNlIG1ldHJpY3MNCmxtX3Jtc2UgPC0gc3FydChtZWFuKChsbV9wcmVkaWN0aW9ucyAtIHRlc3QucmVnLmRhdGEkbWVkdileMikpDQpsbV9tYWUgPC0gbWVhbihhYnMobG1fcHJlZGljdGlvbnMgLSB0ZXN0LnJlZy5kYXRhJG1lZHYpKQ0KbG1fcl9zcXVhcmVkIDwtIGNvcihsbV9wcmVkaWN0aW9ucywgdGVzdC5yZWcuZGF0YSRtZWR2KV4yDQoNCiMjIw0Kcm1zZS5OTjIuaW1wIDwtIChsbV9ybXNlIC0gcm1zZS5OTjIpL2xtX3Jtc2UqMTAwDQptYWUuTk4yLmltcCA8LSAobG1fbWFlIC0gbWFlLk5OMikvbG1fbWFlICogMTAwDQpSc3EuTk4yLmltcCA8LSAocl9zcXVhcmVkLk5OMiAtIGxtX3Jfc3F1YXJlZCkvbG1fcl9zcXVhcmVkICogMTAwDQoNCk5OMi5pbXByb3ZlIDwtYyhybXNlLk5OMi5pbXAsIG1hZS5OTjIuaW1wLCBSc3EuTk4yLmltcCkNCg0KcGVyZi5tYXRyaXggPC1kYXRhLmZyYW1lKA0KICAgICAgICAgICAgICBMTSA9IGMobG1fcm1zZSwgbG1fbWFlLCBsbV9yX3NxdWFyZWQpLA0KICAgICAgICAgICAgICBOTi4xID0gYyhybXNlLk5OMSwgbWFlLk5OMSwgcl9zcXVhcmVkLk5OMSksDQogICAgICAgICAgICAgIE5OLjIgPSBjKHJtc2UuTk4yLCBtYWUuTk4yLCByX3NxdWFyZWQuTk4yKQ0KICAgICAgICAgKQ0KDQpwZXJmLm1hdHJpeCROTjEuSW1wcm92ZSA8LSByb3VuZCgxMDAqKHBlcmYubWF0cml4JExNLXBlcmYubWF0cml4JE5OLjEpL3BlcmYubWF0cml4JExNLDIpDQpwZXJmLm1hdHJpeCROTjIuSW1wcm92ZSA8LSByb3VuZCgxMDAqKHBlcmYubWF0cml4JExNLXBlcmYubWF0cml4JE5OLjIpL3BlcmYubWF0cml4JExNLDIpDQoNCnJvd25hbWVzKHBlcmYubWF0cml4KSA8LSBjKCJSTVNFIiwgIk1BRSIsICJSLnNxIikNCnBhbmRlcihwZXJmLm1hdHJpeCkNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiMgUGxvdCBib3RoIHByZWRpY3Rpb25zDQpjb21wYXJpc29uLmRhdGEuTk4yIDwtIGRhdGEuZnJhbWUoDQogIEFjdHVhbCA9IHRlc3QucmVnLmRhdGEkbWVkdiwNCiAgTUxQID0gcHJlZC5OTjIgLA0KICBMaW5lYXIgPSBsbV9wcmVkaWN0aW9ucw0KKQ0KDQpnZ3Bsb3QoY29tcGFyaXNvbi5kYXRhLk5OMikgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gQWN0dWFsLCB5ID0gTUxQLCBjb2xvciA9ICJNTFAiKSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gQWN0dWFsLCB5ID0gTGluZWFyLCBjb2xvciA9ICJMaW5lYXIgUmVncmVzc2lvbiIpKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJNTFAiID0gImJsdWUiLCAiTGluZWFyIFJlZ3Jlc3Npb24iID0gInJlZCIpKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWwgQ29tcGFyaXNvbjogQWN0dWFsIHZzIFByZWRpY3RlZCIsDQogICAgICAgeCA9ICJBY3R1YWwgVmFsdWVzIiwNCiAgICAgICB5ID0gIlByZWRpY3RlZCBWYWx1ZXMiLA0KICAgICAgIGNvbG9yID0gIk1vZGVsIFR5cGUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KIyBNTFAgQ2xhc3NpZmljYXRpb24NCg0KVGhlIG1vZGVsaW5nIHByb2Nlc3MgZm9yIE1MUCBjbGFzc2lmaWNhdGlvbiBpcyBpZGVudGljYWwgdG8gdGhhdCBvZiBNTFAgcmVncmVzc2lvbi4gQXMgd2UgZGlkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uIG9uIE1MUCByZWdyZXNzaW9uLCB3ZSB3aWxsIGRvY3VtZW50IHRoZSBtb2RlbGluZyBwcm9jZXNzIGluIGRldGFpbC4NCg0KVGhlICoqUGltYSBJbmRpYW4gRGlhYmV0ZXMqKiBkYXRhc2V0IGNvbnRhaW5zIG9ubHkgbnVtZXJpY2FsIGZlYXR1cmUgdmFyaWFibGVzLiBUaGVyZWZvcmUsIHdlIHdpbGwgYXBwbHkgbWluLW1heCBzY2FsaW5nIHRvIGFsbCBmZWF0dXJlcyBhbmQgY29udmVydCB0aGUgdGFyZ2V0IHZhcmlhYmxlIGludG8gYSBmYWN0b3IgdmFyaWFibGUuDQoNCg0KYGBge3J9DQojIExvYWQgbmVjZXNzYXJ5IGxpYnJhcmllcw0KIyBsaWJyYXJ5KG5ldXJhbG5ldCkNCiMgbGlicmFyeShwUk9DKSAgICAgIyBGb3IgUk9DIGFuYWx5c2lzDQojIGxpYnJhcnkoZ2dwbG90MikgICMgRm9yIHZpc3VhbGl6YXRpb24NCg0KIyBMb2FkIHRoZSBQaW1hIEluZGlhbnMgRGlhYmV0ZXMgZGF0YXNldA0KZGF0YSgiUGltYUluZGlhbnNEaWFiZXRlczIiLCBwYWNrYWdlID0gIm1sYmVuY2giKQ0KDQojIyByZW1vdmluZyByZWNvcmRzIHdpdGggbWlzc2luZyBjb21wb25lbnQ7IGltcHV0YXRpb24gc2hvdWxkIGJlIGNvbnNpZGVyZWQgaW4NCiMjIHByYWN0aWNhbCBhcHBsaWNhdGlvbnMNCmRpYWJldGVzLmRhdGEgPC0gbmEub21pdChQaW1hSW5kaWFuc0RpYWJldGVzMikgIA0KDQojIEZlYXR1cmUgc2NhbGluZyAtIG5vcm1hbGl6ZSBudW1lcmljIHZhcmlhYmxlcyB0byBbMCwxXSByYW5nZQ0Kbm9ybWFsaXplIDwtIGZ1bmN0aW9uKHgpIHsNCiAgcmV0dXJuICgoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSkNCn0NCiMgQXBwbHkgbm9ybWFsaXphdGlvbiB0byBhbGwgbnVtZXJpYyBjb2x1bW5zDQpudW1lcmljLmNvbHMgPC0gc2FwcGx5KGRpYWJldGVzLmRhdGEsIGlzLm51bWVyaWMpDQpkaWFiZXRlcy5kYXRhW251bWVyaWMuY29sc10gPC0gbGFwcGx5KGRpYWJldGVzLmRhdGFbbnVtZXJpYy5jb2xzXSwgbm9ybWFsaXplKQ0KDQojIEVuY29kZSB0aGUgdGFyZ2V0IHZhcmlhYmxlIChkaWFiZXRlcykgYXMgbnVtZXJpYyAoMC8xKQ0KZGlhYmV0ZXMuZGF0YSRkaWFiZXRlcyA8LSBpZmVsc2UoZGlhYmV0ZXMuZGF0YSRkaWFiZXRlcyA9PSAicG9zIiwgMSwgMCkNCg0KIyBUd28td2F5IGRhdGEgc3BsaXR0aW5nOiA3MC0zMCUNCnNldC5zZWVkKDEyMykgICMgRm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2FtcGxlLnNpemUuY2xzIDwtIGZsb29yKDAuNzAgKiBucm93KGRpYWJldGVzLmRhdGEpKQ0KdHJhaW4uaW5kaWNlcy5jbHMgPC0gc2FtcGxlKDE6c2FtcGxlLnNpemUuY2xzLCBzaXplID0gc2FtcGxlLnNpemUuY2xzLCByZXBsYWNlID0gRkFMU0UpDQoNCnRyYWluLmRhdGEuY2xzIDwtIGRpYWJldGVzLmRhdGFbdHJhaW4uaW5kaWNlcy5jbHMsIF0NCnRlc3QuZGF0YS5jbHMgPC0gZGlhYmV0ZXMuZGF0YVstdHJhaW4uaW5kaWNlcy5jbHMsIF0NCmBgYA0KDQoNClRvIHNpbXBsaWZ5IGh5cGVycGFyYW1ldGVyIHR1bmluZyBhbmQgZmluYWwgbW9kZWwgdHJhaW5pbmcgd2l0aCB0aGUgcHJlLXNlbGVjdGVkIE1MUCBhcmNoaXRlY3R1cmUgZm9yIGNsYXNzaWZpY2F0aW9uLCB3ZSBkZWZpbmUgYSBjdXN0b20gZnVuY3Rpb24gdG8gZGV0ZXJtaW5lIHRoZSBvcHRpbWFsIG51bWJlciBvZiBub2RlcyBmb3IgYm90aCBzaW5nbGUtaGlkZGVuLWxheWVyIGFuZCBkb3VibGUtaGlkZGVuLWxheWVyIE1MUHMuIFRoZSBjcml0ZXJpb24gZm9yIHNlbGVjdGluZyB0aGUgb3B0aW1hbCBudW1iZXIgb2Ygbm9kZXMgaXMgdGhlIGFyZWEgdW5kZXIgdGhlIFJPQyBjdXJ2ZSAoQVVDKSBhcyB0aGUgZXZhbHVhdGlvbiBtZXRyaWMsIHRob3VnaCBhY2N1cmFjeSBiYXNlZCBvbiB0aGUgZGVmYXVsdCAwLjUgdGhyZXNob2xkcyBjb3VsZCBhbHRlcm5hdGl2ZWx5IGJlIHVzZWQuIFdlIGNob29zZSB0aGUgbG9naXN0aWMgYWN0aXZhdGlvbiBmdW5jdGlvbiB3aGlsZSBrZWVwaW5nIGFsbCBvdGhlciBoeXBlcnBhcmFtZXRlcnMgYXQgdGhlaXIgZGVmYXVsdCB2YWx1ZXMuDQoNCg0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIHBlcmZvcm0gZ3JpZCBzZWFyY2ggZm9yIG5ldXJhbG5ldA0KbmV1cmFsbmV0LmdyaWQuc2VhcmNoIDwtIGZ1bmN0aW9uKHRyYWluLmRhdGEsIHRlc3QuZGF0YSwgaGlkZGVuLmxheWVycyA9IDEpIHsNCiAgIyBEZWZpbmUgdGhlIGdyaWQgb2YgaHlwZXJwYXJhbWV0ZXJzDQogIGlmIChoaWRkZW4ubGF5ZXJzID09IDEpIHsNCiAgICBoaWRkZW4ubm9kZXMgPC0gYygyLCA0LCA2LCA4LCAxMCkNCiAgICBncmlkIDwtIGV4cGFuZC5ncmlkKGhpZGRlbiA9IGhpZGRlbi5ub2RlcykNCiAgfSBlbHNlIHsNCiAgICBoaWRkZW4ubm9kZXMgPC0gYygyLCA0LCA2KQ0KICAgIGdyaWQgPC0gZXhwYW5kLmdyaWQoaGlkZGVuMSA9IGhpZGRlbi5ub2RlcywgaGlkZGVuMiA9IGhpZGRlbi5ub2RlcykNCiAgfQ0KICANCiAgIyBBZGQgY29sdW1ucyB0byBzdG9yZSByZXN1bHRzDQogIGdyaWQkYWNjdXJhY3kgPC0gTkENCiAgZ3JpZCRhdWMgPC0gTkENCiAgDQogICMgRm9ybXVsYSBmb3IgbmV1cmFsIG5ldHdvcmsNCiAgbm4uZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlKCJkaWFiZXRlcyB+IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKG5hbWVzKHRyYWluLmRhdGEpWyFuYW1lcyh0cmFpbi5kYXRhKSAlaW4lICJkaWFiZXRlcyJdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiICsgIikpKQ0KICANCiAgIyBQZXJmb3JtIGdyaWQgc2VhcmNoDQogIGZvciAoaSBpbiAxOm5yb3coZ3JpZCkpIHsNCiAgICBpZiAoaGlkZGVuLmxheWVycyA9PSAxKSB7DQogICAgICBoaWRkZW4gPC0gYyhncmlkJGhpZGRlbltpXSkNCiAgICB9IGVsc2Ugew0KICAgICAgaGlkZGVuIDwtIGMoZ3JpZCRoaWRkZW4xW2ldLCBncmlkJGhpZGRlbjJbaV0pDQogICAgfQ0KICAgIA0KICAgICMgVHJhaW4gdGhlIG1vZGVsDQogICAgbm4ubW9kZWwgPC0gbmV1cmFsbmV0KA0KICAgICAgZm9ybXVsYSA9IG5uLmZvcm11bGEsDQogICAgICBkYXRhID0gdHJhaW4uZGF0YSwNCiAgICAgIGhpZGRlbiA9IGhpZGRlbiwNCiAgICAgIGxpbmVhci5vdXRwdXQgPSBGQUxTRSwgICMgRm9yIGNsYXNzaWZpY2F0aW9uDQogICAgICBhY3QuZmN0ID0gImxvZ2lzdGljIiwgICAjIFNpZ21vaWQgYWN0aXZhdGlvbg0KICAgICAgc3RlcG1heCA9IDFlNiAgICAgICAgICAgIyBJbmNyZWFzZSBtYXggc3RlcHMgZm9yIGNvbnZlcmdlbmNlDQogICAgKQ0KICAgIA0KICAgICMgTWFrZSBwcmVkaWN0aW9ucw0KICAgIHByZWRpY3Rpb25zIDwtIHByZWRpY3Qobm4ubW9kZWwsIHRlc3QuZGF0YSkNCiAgICBwcmVkaWN0ZWQuY2xhc3NlcyA8LSBpZmVsc2UocHJlZGljdGlvbnMgPiAwLjUsIDEsIDApDQogICAgDQogICAgIyBDYWxjdWxhdGUgYWNjdXJhY3kNCiAgICBhY2N1cmFjeSA8LSBtZWFuKHByZWRpY3RlZC5jbGFzc2VzID09IHRlc3QuZGF0YSRkaWFiZXRlcykNCiAgICANCiAgICAjIENhbGN1bGF0ZSBBVUMNCiAgICByb2Mub2JqIDwtIHJvYyh0ZXN0LmRhdGEkZGlhYmV0ZXMsIHByZWRpY3Rpb25zKQ0KICAgIGF1Yy52YWx1ZSA8LSBhdWMocm9jLm9iaikNCiAgICANCiAgICAjIFN0b3JlIHJlc3VsdHMNCiAgICBncmlkJGFjY3VyYWN5W2ldIDwtIGFjY3VyYWN5DQogICAgZ3JpZCRhdWNbaV0gPC0gYXVjLnZhbHVlDQogIH0NCiAgcmV0dXJuKGdyaWQpDQp9DQpgYGANCg0KDQpUaGUgcGVyZm9ybWFuY2UgdGFibGUgb2YgY29ycmVzcG9uZGluZyBudW1iZXIgb2Ygbm9kZXMgaW4gb25lLWhpZGRlbi1sYXllciBNTFAgaXMgZ2l2ZW4gYmVsb3cuDQoNCmBgYHtyfQ0KIyBQZXJmb3JtIGdyaWQgc2VhcmNoIGZvciBzaW5nbGUgaGlkZGVuIGxheWVyDQpncmlkLnJlc3VsdHMuMWxheWVyIDwtIG5ldXJhbG5ldC5ncmlkLnNlYXJjaCh0cmFpbi5kYXRhPXRyYWluLmRhdGEuY2xzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3QuZGF0YT10ZXN0LmRhdGEuY2xzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbi5sYXllcnMgPSAxKQ0KcGFuZGVyKGdyaWQucmVzdWx0cy4xbGF5ZXIpDQpgYGANCg0KVGhlIG9wdGltYWwgbnVtYmVyIG9mIG5vZGVzIGluIHRoZSBoaWRkZW4gbGF5ZXIgaXMgdGhlIGNvcnJlc3BvbmRzIHRvIHRoZSBzbWFsbGVzdCBBVUMuIFNpbWlsYXJseSwgdGhlIHBlcmZvcm1hbmNlIHRhYmxlIG9mIHR3by1oaWRkZW4tbGF5ZXIgTUxQIGlzIGdpdmVuIGJlbG93LiANCg0KYGBge3J9DQojIFBlcmZvcm0gZ3JpZCBzZWFyY2ggZm9yIHR3byBoaWRkZW4gbGF5ZXJzDQpncmlkLnJlc3VsdHMuMmxheWVyIDwtIG5ldXJhbG5ldC5ncmlkLnNlYXJjaCh0cmFpbi5kYXRhPXRyYWluLmRhdGEuY2xzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3QuZGF0YT10ZXN0LmRhdGEuY2xzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbi5sYXllcnMgPSAyKQ0KcGFuZGVyKGdyaWQucmVzdWx0cy4ybGF5ZXIpDQpgYGANCg0KKipPbmUtaGlkZGVuLWxheWVyIE1MUCoqDQoNCldlIHVzZSB0aGUgb3B0aW1hbCBudW1iZXIgb2Ygbm9kZXMgdG8gZml0dGhlIG9uZWhpZGRlbi1sYXllciBNTFAgdG8gdGhlIGRhdGEgYW5mIG9idGFpbg0KDQoNCmBgYHtyfQ0KIyBGb3JtdWxhIGZvciBuZXVyYWwgbmV0d29yaw0Kbm4uZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlKCJkaWFiZXRlcyB+IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShuYW1lcyh0cmFpbi5kYXRhLmNscylbIW5hbWVzKHRyYWluLmRhdGEuY2xzKSAlaW4lICJkaWFiZXRlcyJdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiArICIpKSkNCg0KIyBUcmFpbiBzaW5nbGUgaGlkZGVuIGxheWVyIG1vZGVsICh1c2luZyBiZXN0IGNvbmZpZ3VyYXRpb24gZnJvbSBncmlkIHNlYXJjaCkNCmJlc3QuMWxheWVyIDwtIGdyaWQucmVzdWx0cy4xbGF5ZXJbd2hpY2gubWF4KGdyaWQucmVzdWx0cy4xbGF5ZXIkYXVjKSwgXQ0KDQpubi4xbGF5ZXIgPC0gbmV1cmFsbmV0KA0KICBmb3JtdWxhID0gbm4uZm9ybXVsYSwNCiAgZGF0YSA9IHRyYWluLmRhdGEuY2xzLA0KICBoaWRkZW4gPSBiZXN0LjFsYXllciRoaWRkZW4sDQogIGxpbmVhci5vdXRwdXQgPSBGQUxTRSwNCiAgYWN0LmZjdCA9ICJsb2dpc3RpYyIsDQogIHN0ZXBtYXggPSAxZTYNCikNCiMjDQpwbG90KG5uLjFsYXllciwgbWFpbiA9IHBhc3RlKCJPbmUtaGlkZGVuLWxheWVyIHdpdGgiLCBiZXN0LjFsYXllciRoaWRkZW4sICJOb2RlcyIpKQ0KYGBgDQoNCg0KYGBge3J9DQojIFRyYWluIHR3byBoaWRkZW4gbGF5ZXJzIG1vZGVsICh1c2luZyBiZXN0IGNvbmZpZ3VyYXRpb24gZnJvbSBncmlkIHNlYXJjaCkNCmJlc3QuMmxheWVyIDwtIGdyaWQucmVzdWx0cy4ybGF5ZXJbd2hpY2gubWF4KGdyaWQucmVzdWx0cy4ybGF5ZXIkYXVjKSwgXQ0KDQpubi4ybGF5ZXIgPC0gbmV1cmFsbmV0KA0KICBmb3JtdWxhID0gbm4uZm9ybXVsYSwNCiAgZGF0YSA9IHRyYWluLmRhdGEuY2xzLA0KICBoaWRkZW4gPSBjKGJlc3QuMmxheWVyJGhpZGRlbjEsIGJlc3QuMmxheWVyJGhpZGRlbjIpLA0KICBsaW5lYXIub3V0cHV0ID0gRkFMU0UsDQogIGFjdC5mY3QgPSAibG9naXN0aWMiLA0KICBzdGVwbWF4ID0gMWU2DQopDQojIw0KcGxvdChubi4ybGF5ZXIpDQpgYGANCg0KDQpJbiB0aGUgdHdvIG1vZGVsIHBsb3RzIGFib3ZlLCB0aGUgKipFcnJvcioqIGFuZCAqKlN0ZXBzKiogdmFsdWVzIGRpc3BsYXllZCBhdCB0aGUgYm90dG9tIHJlcHJlc2VudDoNCg0KKiAqKlN0ZXBzKio6DQoNClRoZSBudW1iZXIgb2YgKip0cmFpbmluZyBpdGVyYXRpb25zKiogKGVwb2NocykgY29tcGxldGVkIGR1cmluZyBtb2RlbCBvcHRpbWl6YXRpb24uIEVhY2ggc3RlcCBjb3JyZXNwb25kcyB0byBvbmUgY29tcGxldGUgZm9yd2FyZC9iYWNrd2FyZCBwYXNzIGFuZCB3ZWlnaHQgdXBkYXRlIGN5Y2xlLg0KDQoqICoqRXJyb3JzKio6DQoNClRoZSAqKnRyYWluaW5nIGVycm9yKiogcmVmbGVjdHMgdGhlIGxvc3MgZnVuY3Rpb24gdmFsdWUgKHR5cGljYWxseSBTU0UgZm9yIHJlZ3Jlc3Npb24gb3IgY3Jvc3MtZW50cm9weSBmb3IgY2xhc3NpZmljYXRpb24pLiBUaGUgZGlzcGxheWVkICoqRXJyb3IqKiByZXByZXNlbnRzIHRoZSBmaW5hbCBlcnJvciB2YWx1ZSBhY2hpZXZlZCB3aGVuIHRoZSBvcHRpbWl6YXRpb24gcHJvY2VzcyBjb252ZXJnZXMuDQoNCg0KTmV4dCwgd2Ugd3JpdGUgYSBjdXN0b20gZnVuY3Rpb24gdG8gZXh0cmFjdCB0aGUgcGVyZm9ybWFuY2UgbWV0cmljcyB0byBhc3Nlc3MgdGhlIGdsb2JhbCBwZXJmb3JtYW5jZSB0aHJvdWdoIFJPQyBjdXJ2ZXMgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGFyZWFzIHVuZGVyIHRoZSBST0MgY3VydmVzLg0KDQpgYGB7cn0NCiMgRnVuY3Rpb24gdG8gZXZhbHVhdGUgbW9kZWwgcGVyZm9ybWFuY2UNCmV2YWx1YXRlLm1vZGVsIDwtIGZ1bmN0aW9uKG1vZGVsLCB0ZXN0LmRhdGEpIHsNCiAgIyBNYWtlIHByZWRpY3Rpb25zDQogIHByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIHRlc3QuZGF0YSkNCiAgcHJlZGljdGVkLmNsYXNzZXMgPC0gaWZlbHNlKHByZWRpY3Rpb25zID4gMC41LCAxLCAwKQ0KICANCiAgIyBDYWxjdWxhdGUgbWV0cmljcw0KICBhY2N1cmFjeSA8LSBtZWFuKHByZWRpY3RlZC5jbGFzc2VzID09IHRlc3QuZGF0YSRkaWFiZXRlcykNCiAgY29uZnVzaW9uLm1hdHJpeCA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkaWN0ZWQuY2xhc3NlcywgQWN0dWFsID0gdGVzdC5kYXRhJGRpYWJldGVzKQ0KICByb2Mub2JqIDwtIHJvYyh0ZXN0LmRhdGEkZGlhYmV0ZXMsIHByZWRpY3Rpb25zKQ0KICBhdWMudmFsdWUgPC0gYXVjKHJvYy5vYmopDQogIA0KICByZXR1cm4obGlzdCgNCiAgICBhY2N1cmFjeSA9IGFjY3VyYWN5LA0KICAgIGNvbmZ1c2lvbi5tYXRyaXggPSBjb25mdXNpb24ubWF0cml4LA0KICAgIHJvYy5vYmogPSByb2Mub2JqLA0KICAgIGF1YyA9IGF1Yy52YWx1ZQ0KICApKQ0KfQ0KDQojIEV2YWx1YXRlIHNpbmdsZSBoaWRkZW4gbGF5ZXIgbW9kZWwNCnBlcmYuMWxheWVyIDwtIGV2YWx1YXRlLm1vZGVsKG5uLjFsYXllciwgdGVzdC5kYXRhLmNscykNCiNwcmludChwZXJmLjFsYXllcltjKCJhY2N1cmFjeSIsICJjb25mdXNpb25fbWF0cml4IiwgImF1YyIpXSkNCg0KIyBFdmFsdWF0ZSB0d28gaGlkZGVuIGxheWVycyBtb2RlbA0KcGVyZi4ybGF5ZXIgPC0gZXZhbHVhdGUubW9kZWwobm4uMmxheWVyLCB0ZXN0LmRhdGEuY2xzKQ0KI3ByaW50KHBlcmYuMmxheWVyW2MoImFjY3VyYWN5IiwgImNvbmZ1c2lvbl9tYXRyaXgiLCAiYXVjIildKQ0KYGBgDQoNCg0KV2UgdXNlIGNsYXNzaWMgbG9naXN0aWMgcmVncmVzc2lvbiBhcyB0aGUgYmFzZSBtb2RlbCBhbmQgY29tcGFyZSBpdCB3aXRoIHRoZSB0d28gTUxQcyB1c2luZyBST0MgY3VydmVzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIEFVQyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuDQoNCg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9DQojIFRyYWluIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgKGJhc2UgbW9kZWwpDQpsb2dpdC5tb2RlbCA8LSBnbG0oZGlhYmV0ZXMgfiAuLCBkYXRhID0gdHJhaW4uZGF0YS5jbHMsIGZhbWlseSA9IGJpbm9taWFsKQ0KDQojIEV2YWx1YXRlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCmxvZ2l0LnByZWQgPC0gcHJlZGljdChsb2dpdC5tb2RlbCwgdGVzdC5kYXRhLmNscywgdHlwZSA9ICJyZXNwb25zZSIpDQpsb2dpdC5jbGFzc2VzIDwtIGlmZWxzZShsb2dpdC5wcmVkID4gMC41LCAxLCAwKQ0KbG9naXQuYWNjdXJhY3kgPC0gbWVhbihsb2dpdC5jbGFzc2VzID09IHRlc3QuZGF0YS5jbHMkZGlhYmV0ZXMpDQpsb2dpdC5yb2MgPC0gcm9jKHRlc3QuZGF0YS5jbHMkZGlhYmV0ZXMsIGxvZ2l0LnByZWQpDQpsb2dpdC5hdWMgPC0gYXVjKGxvZ2l0LnJvYykNCg0KIyMNCnJvYy4xbGF5ZXIgPC0gcGVyZi4xbGF5ZXIkcm9jLm9iag0Kcm9jLjJsYXllciA8LSBwZXJmLjJsYXllciRyb2Mub2JqDQpyb2MubG9naXQgPC0gbG9naXQucm9jDQoNCiMjIHNwZWNpZmljaXR5IGFuZCBzZW5zaXRpdml0eQ0Kc2VuLjFsYXllciA8LSByb2MuMWxheWVyJHNlbnNpdGl2aXRpZXMNCnNwZS4xbGF5ZXIgPC0gcm9jLjFsYXllciRzcGVjaWZpY2l0aWVzDQpzZW4uMmxheWVyIDwtIHJvYy4ybGF5ZXIkc2Vuc2l0aXZpdGllcw0Kc3BlLjJsYXllciA8LSByb2MuMmxheWVyJHNwZWNpZmljaXRpZXMNCnNlbi5sb2dpdCA8LSByb2MubG9naXQkc2Vuc2l0aXZpdGllcw0Kc3BlLmxvZ2l0IDwtIHJvYy5sb2dpdCRzcGVjaWZpY2l0aWVzDQoNCiMjIEFVQw0KYXVjLjFsYXllciA8LSByb2MuMWxheWVyJGF1Yw0KYXVjLjJsYXllciA8LSByb2MuMmxheWVyJGF1Yw0KYXVjLmxvZ2l0IDwtIHJvYy5sb2dpdCRhdWMNCg0KIyMgUGxvdCBST0MgY3VydmVzIGZvciBjb21wYXJpc29uDQpwYXIocHR5ID0gInMiKSAgICMgbWFrZSBhIHNxdWFyZSBwbG90IHRvIGF2YW9pZCBkaXN0b3J0aW9uDQpwbG90KDEtc3BlLjFsYXllciwgc2VuLjFsYXllciwgdHlwZSA9ICJsIiwgbHR5ID0gMSwNCiAgICAgY29sID0gImJsdWUiLCANCiAgICAgeGxhYiA9ICIxIC0gc3BlY2lmaWNpdHkiLA0KICAgICB5bGFiID0gInNlbnNpdHZpdHkiLA0KICAgICBtYWluID0gIlJPQyBDdXJ2ZSBDb21wYXJpc29uIikNCg0KbGluZXMoMS1zcGUuMmxheWVyLCBzZW4uMmxheWVyLCBsdHkgPSAxLCBjb2wgPSAiZGFya3JlZCIpDQpsaW5lcygxLXNwZS5sb2dpdCwgc2VuLmxvZ2l0LCBsdHkgPSAxLCBjb2wgPSAiZGFya2dyZWVuIikNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCANCiAgICAgICBsZWdlbmQgPSBjKHBhc3RlKCIxLWxheWVyIE1MUCAoQVVDID0iLCByb3VuZChwZXJmLjFsYXllciRhdWMsIDMpLCAiKSIpLA0KICAgICAgICAgICAgICAgICAgcGFzdGUoIjItbGF5ZXIgTUxQIChBVUMgPSIsIHJvdW5kKHBlcmYuMmxheWVyJGF1YywgMyksICIpIiksDQogICAgICAgICAgICAgICAgICBwYXN0ZSgiTG9naXN0aWMgUmVnIChBVUMgPSIsIHJvdW5kKGxvZ2l0LmF1YywgMyksICIpIikpLA0KICAgICAgICAgICAgICAgIGNvbCA9IGMoImJsdWUiLCAiZGFya3JlZCIsICJkYXJrZ3JlZW4iKSwgDQogICAgICAgICAgICAgICAgbHR5ID0gMSwgY2V4ID0gMC43LCBidHkgPSAibiIpDQpgYGANCg0KVGhlIFJPQyBhbmFseXNpcyBzaG93cyB0aGF0IHRoZSBjbGFzc2ljIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgcGVyZm9ybXMgc2xpZ2h0bHkgYmV0dGVyIHRoYW4gdGhlIHR3byBNTFBzLiBUaGUgdHdvIE1MUHMgcGVyZm9ybSBlcXVhbGx5IHdlbGwsIGV2ZW4gdGhvdWdoIHRoZSB0d28taGlkZGVuLWxheWVyIE1MUCBoYXMgbW9yZSBwYXJhbWV0ZXJzICh3ZWlnaHRzKSBhbmQgaXMgbW9yZSBjb21wbGV4IHRoYW4gdGhlIHNpbmdsZS1oaWRkZW4tbGF5ZXIgTUxQLiBJbiBnZW5lcmFsLCBhIHNpbmdsZS1oaWRkZW4tbGF5ZXIgTUxQIGlzIHJlY29tbWVuZGVkIGlmIGFuIE1MUCBpcyB1c2VkIGZvciBjbGFzc2lmaWNhdGlvbi4NCg0KDQoNCiMgTUxQIG9yIENsYXNzaWNhbCBNb2RlbHM/DQoNCg0KV2UgaGF2ZSBkaXNjdXNzZWQgdGhlIE1MUCBhcmNoaXRlY3R1cmUgYW5kIHRoZSBwcm9jZXNzIG9mIGltcGxlbWVudGluZyBNTFBzIGFsb25nc2lkZSBjbGFzc2ljIG1vZGVscy4gQ2FzZSBzdHVkaWVzIHVzaW5nIHRoZSAqKkJvc3RvbiBIb3VzaW5nIERhdGEqKiBkZW1vbnN0cmF0ZSB0aGF0IE1MUCByZWdyZXNzaW9uIG91dHBlcmZvcm1zIGNsYXNzaWMgbGluZWFyIHJlZ3Jlc3Npb24sIHdoaWxlIHRoZSBjbGFzc2ljIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcGVyZm9ybXMgYmV0dGVyIHRoYW4gTUxQIGNsYXNzaWZpY2F0aW9uIGluIHRoZSAqKlBpbWEgSW5kaWFuIERpYWJldGVzKiogZGF0YXNldCBhbmFseXNpcy4gTmV4dCwgd2Ugd2lsbCBwcmVzZW50IGEgZ2VuZXJhbCBjb21wYXJpc29uIGJldHdlZW4gdGhlc2UgbW9kZWxzLg0KDQpcDQoNCioqTUxQIENsYXNzaWZpY2F0aW9uIHZzLiBMb2dpc3RpYyBSZWdyZXNzaW9uKioNCg0KVGhlIGNvbXBhcmlzb24gYmV0d2VlbiBtdWx0aWxheWVyIHBlcmNlcHRyb24gKE1MUCkgY2xhc3NpZmllcnMgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gaGlnaGxpZ2h0cyBhIGZ1bmRhbWVudGFsIHRyYWRlLW9mZiBiZXR3ZWVuIGZsZXhpYmlsaXR5IGFuZCBzaW1wbGljaXR5LiANCg0KKiBMb2dpc3RpYyByZWdyZXNzaW9uLCBhcyBhIGxpbmVhciBjbGFzc2lmaWVyLCBwZXJmb3JtcyB3ZWxsIHdoZW4gZGVjaXNpb24gYm91bmRhcmllcyBhcmUgYXBwcm94aW1hdGVseSBsaW5lYXIsIG9mZmVyaW5nIGhpZ2ggaW50ZXJwcmV0YWJpbGl0eSwgY29tcHV0YXRpb25hbCBlZmZpY2llbmN5LCBhbmQgcm9idXN0bmVzcyB0byBvdmVyZml0dGluZ+KAlHBhcnRpY3VsYXJseSB3aXRoIGxpbWl0ZWQgZGF0YS4gDQoNCiogTUxQcywgd2l0aCB0aGVpciBub25saW5lYXIgYWN0aXZhdGlvbiBmdW5jdGlvbnMsIGNhbiBtb2RlbCBtb3JlIGNvbXBsZXggZGVjaXNpb24gYm91bmRhcmllcyBidXQgcmVxdWlyZSBjYXJlZnVsIHR1bmluZyBvZiBhcmNoaXRlY3R1cmUgYW5kIHJlZ3VsYXJpemF0aW9uIHRvIGF2b2lkIG92ZXJmaXR0aW5nLiANCg0KKiBUaGUgYWRkZWQgY29tcGxleGl0eSBvZiBuZXVyYWwgbmV0d29ya3MgbWF5IG5vdCBhbHdheXMgYmUganVzdGlmaWVkIGZvciBzaW1wbGVyIGNsYXNzaWZpY2F0aW9uIHRhc2tzLiBIb3dldmVyLCBNTFBzIHJlbWFpbiB2YWx1YWJsZSB3aGVuIGRlYWxpbmcgd2l0aCBoaWdobHkgbm9ubGluZWFyIGRhdGEgd2hlcmUgbG9naXN0aWMgcmVncmVzc2lvbuKAmXMgbGluZWFyIGFzc3VtcHRpb25zIGZhaWwuDQoNClwNCg0KKipNTFAgUmVncmVzc2lvbiB2cy4gQ2xhc3NpYyBMaW5lYXIgUmVncmVzc2lvbioqDQoNClNpbWlsYXIgdG8gdGhlaXIgY2xhc3NpZmljYXRpb24gY291bnRlcnBhcnRzLCBNTFAgcmVncmVzc2lvbiBtb2RlbHMgYW5kIGNsYXNzaWNhbCBsaW5lYXIgcmVncmVzc2lvbiBzZXJ2ZSBkaWZmZXJlbnQgcHVycG9zZXMgZGVwZW5kaW5nIG9uIHRoZSBuYXR1cmUgb2YgdGhlIGRhdGEuIA0KDQoqIExpbmVhciByZWdyZXNzaW9uIGlzIG9wdGltYWwgd2hlbiByZWxhdGlvbnNoaXBzIGJldHdlZW4gcHJlZGljdG9ycyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZSBhcmUgbGluZWFyLCBwcm92aWRpbmcgaW50ZXJwcmV0YWJsZSBjb2VmZmljaWVudHMsIGZhc3QgdHJhaW5pbmcsIGFuZCBsb3cgcmlzayBvZiBvdmVyZml0dGluZy4gDQoNCiogTUxQIHJlZ3Jlc3Npb24sIGhvd2V2ZXIsIGV4Y2VscyBpbiBjYXB0dXJpbmcgbm9ubGluZWFyIGFuZCBpbnRlcmFjdGl2ZSBlZmZlY3RzLCBtYWtpbmcgaXQgc3VpdGFibGUgZm9yIG1vcmUgY29tcGxleCByZWdyZXNzaW9uIHRhc2tzIHdoZXJlIGxpbmVhciBtb2RlbHMgdW5kZXJwZXJmb3JtLiANCg0KKiBUaGUgdHJhZGUtb2ZmIGxpZXMgaW4gY29tcHV0YXRpb25hbCBjb3N0LCB0dW5pbmcgY29tcGxleGl0eSwgYW5kIHRoZSBuZWVkIGZvciBsYXJnZXIgZGF0YXNldHMgdG8gcHJldmVudCAqKm92ZXJmaXR0aW5nKiouIElmIHRoZSB1bmRlcmx5aW5nIGRhdGEgc3RydWN0dXJlIGlzIHVua25vd24sIGEgcHJhY3RpY2FsIGFwcHJvYWNoIGlzIHRvIGJlZ2luIHdpdGggbGluZWFyIHJlZ3Jlc3Npb24gYXMgYSBiYXNlbGluZSBiZWZvcmUgY29uc2lkZXJpbmcgTUxQcyBpZiByZXNpZHVhbHMgc3VnZ2VzdCB1bm1vZGVsZWQgbm9ubGluZWFyaXR5Lg0KDQpcDQoNCioqU2luZ2xlLUhpZGRlbi1MYXllciB2cy4gVHdvLUhpZGRlbi1MYXllciBNTFBzKioNCg0KVGhlIGNvbXBhcmlzb24gYmV0d2VlbiAqKnNpbmdsZS0qKiBhbmQgKip0d28taGlkZGVuLWxheWVyIE1MUHMqKiB1bmRlcnNjb3JlcyBtb2RlbCBjb21wbGV4aXR5LiANCg0KKiAqKkRlZXBlciBuZXR3b3JrcyoqIHRoZW9yZXRpY2FsbHkgaGF2ZSBncmVhdGVyIHJlcHJlc2VudGF0aW9uYWwgcG93ZXIuIENhc2Ugc3R1ZGllcyBmb3VuZCB0aGF0IHRoZSAqKnR3by1oaWRkZW4tbGF5ZXIqKiBNTFAgZGlkIG5vdCBvdXRwZXJmb3JtIGl0cyBzaW1wbGVyIGNvdW50ZXJwYXJ0LCBkZXNwaXRlIGhhdmluZyBtb3JlIHBhcmFtZXRlcnMuIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIHRoZSB1bml2ZXJzYWwgYXBwcm94aW1hdGlvbiB0aGVvcmVtLCB3aGljaCBzdGF0ZXMgdGhhdCBhIHNpbmdsZSBoaWRkZW4gbGF5ZXIgKHdpdGggc3VmZmljaWVudCBuZXVyb25zKSBjYW4gYXBwcm94aW1hdGUgYW55IGNvbnRpbnVvdXMgZnVuY3Rpb24uIA0KDQoqIFRoZSBhZGRpdGlvbmFsIGxheWVyIGludHJvZHVjZWQgdW5uZWNlc3NhcnkgY29tcGxleGl0eSB3aXRob3V0IGdhaW5zIGluIGFjY3VyYWN5LCByZWluZm9yY2luZyB0aGF0IGRlZXBlciBhcmNoaXRlY3R1cmVzIHNob3VsZCBvbmx5IGJlIGV4cGxvcmVkIHdoZW4gc2ltcGxlciBtb2RlbHMgcHJvdmUgaW5hZGVxdWF0ZeKAlHR5cGljYWxseSBpbiBoaWdobHkgbm9ubGluZWFyIG9yIGhpZ2gtZGltZW5zaW9uYWwgcHJvYmxlbXMuDQoNClwNCg0KKipHZW5lcmFsIFJlY29tbWVuZGF0aW9ucyoqDQoNCiAgKyAqKlN0YXJ0IHNpbXBsZSoqOiBBbHdheXMgYmVnaW4gd2l0aCBjbGFzc2ljYWwgbW9kZWxzIChsaW5lYXIvbG9naXN0aWMgcmVncmVzc2lvbikgYXMgYmFzZWxpbmVzLiBUaGVpciBpbnRlcnByZXRhYmlsaXR5IGFuZCBlZmZpY2llbmN5IG1ha2UgdGhlbSBpZGVhbCBmb3IgaW5pdGlhbCBleHBsb3JhdGlvbi4NCg0KICArICoqVXNlIE1MUHMganVkaWNpb3VzbHkqKjogUmVzZXJ2ZSBNTFBzIGZvciBjYXNlcyB3aGVyZSBsaW5lYXIgbW9kZWxzIHVuZGVycGVyZm9ybSBkdWUgdG8gY2xlYXIgbm9ubGluZWFyaXR5IG9yIGludGVyYWN0aW9uIGVmZmVjdHMuIFByZWZlciBzaW5nbGUtaGlkZGVuLWxheWVyIGFyY2hpdGVjdHVyZXMgdW5sZXNzIGRlZXBlciBuZXR3b3JrcyBkZW1vbnN0cmFibHkgaW1wcm92ZSBwZXJmb3JtYW5jZS4NCg0KICArICoqQmFsYW5jZSBjb21wbGV4aXR5IGFuZCBwZXJmb3JtYW5jZSoqOiBBdm9pZCB1bm5lY2Vzc2FyeSBtb2RlbCBjb21wbGV4aXR54oCUZmF2b3IgdGhlIHNpbXBsZXN0IG1vZGVsIHRoYXQgYWNoaWV2ZXMgc2F0aXNmYWN0b3J5IGFjY3VyYWN5Lg0KDQogICsgKipIeWJyaWQgYXBwcm9hY2gqKjogQ29tYmluZSBsaW5lYXIgbW9kZWxzIHdpdGggZmVhdHVyZSBlbmdpbmVlcmluZyBvciBrZXJuZWwgbWV0aG9kcyBiZWZvcmUgcmVzb3J0aW5nIHRvIE1MUHMsIHBhcnRpY3VsYXJseSBpbiByZXNvdXJjZS1jb25zdHJhaW5lZCBzZXR0aW5ncy4NCg0KDQoNClVsdGltYXRlbHksIG1vZGVsIGNob2ljZSBzaG91bGQgYmUgZ3VpZGVkIGJ5IHByb2JsZW0gY29uc3RyYWludHMsIGRhdGEgc3RydWN0dXJlLCBhbmQgdGhlIHRyYWRlLW9mZnMgYmV0d2VlbiBhY2N1cmFjeSwgaW50ZXJwcmV0YWJpbGl0eSwgYW5kIGNvbXB1dGF0aW9uYWwgY29zdC4gQSBzeXN0ZW1hdGljLCBpdGVyYXRpdmUgYXBwcm9hY2jigJRmcm9tIGxpbmVhciBtb2RlbHMgdG8gc2hhbGxvdyB0aGVuIGRlZXAgbmV0d29ya3PigJRlbnN1cmVzIGJvdGggZWZmaWNpZW5jeSBhbmQgcGVyZm9ybWFuY2UuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=